提交 1f1c527f authored 作者: kxjia's avatar kxjia

完善问题

上级 89bf837f
...@@ -7,10 +7,9 @@ ...@@ -7,10 +7,9 @@
<a-space> <a-space>
<a-button v-if="props.showApprovalPanel" type="primary" ghost @click="handleApproval">审批</a-button> <a-button v-if="props.showApprovalPanel" type="primary" ghost @click="handleApproval">审批</a-button>
<a-button v-else type="primary" ghost @click="handleSend">发送</a-button> <a-button v-else type="primary" ghost @click="handleSend">发送</a-button>
<a-button type="primary" ghost @click="handleReject">驳回</a-button> <a-button v-show="!props.showApprovalPanel" type="primary" ghost @click="handleReject">驳回</a-button>
<a-button type="primary" ghost @click="handleTransmit">转办</a-button> <a-button type="primary" ghost @click="handleTransmit">转办</a-button>
<a-button type="primary" ghost @click="handleRead">转阅</a-button> <a-button type="primary" ghost @click="handleRead">转阅</a-button>
</a-space> </a-space>
</div> </div>
</template> </template>
...@@ -24,15 +23,14 @@ ...@@ -24,15 +23,14 @@
:current-flow-node="editableNode" :current-flow-node="editableNode"
:showApprovalPanel="props.showApprovalPanel" :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" /> <ApprovalModal @register="registerApprovalModal" @success="updateApprovalSuccess" @approval-fail="updateApprovalFail"/>
<TaskModal @register="registerTaskModal" @success="handleFormDataUpdate"/> <TaskModal @register="registerTaskModal" @success="handleFormDataUpdate" />
</div> </div>
</template> </template>
...@@ -42,8 +40,9 @@ import ApprovalModal from './components/ApprovalModal.vue'; ...@@ -42,8 +40,9 @@ import ApprovalModal from './components/ApprovalModal.vue';
import TaskModal from './components/TaskModal.vue'; import TaskModal from './components/TaskModal.vue';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
const [registerApprovalModal, {openModal}] = useModal();
const [registerTaskModal, {openModal: taskOpenModal}] = useModal(); const [registerApprovalModal, { openModal }] = useModal();
const [registerTaskModal, { openModal: taskOpenModal }] = useModal();
interface WorkflowNode { interface WorkflowNode {
id: string id: string
...@@ -53,8 +52,6 @@ interface WorkflowNode { ...@@ -53,8 +52,6 @@ interface WorkflowNode {
[key: string]: any [key: string]: any
} }
// 表单组件实例类型
interface FormComponentInstance extends ComponentPublicInstance { interface FormComponentInstance extends ComponentPublicInstance {
validate?: () => Promise<any> validate?: () => Promise<any>
getFormData?: () => any getFormData?: () => any
...@@ -64,127 +61,68 @@ interface FormComponentInstance extends ComponentPublicInstance { ...@@ -64,127 +61,68 @@ interface FormComponentInstance extends ComponentPublicInstance {
} }
const props = defineProps({ const props = defineProps({
editableNode: { editableNode: { type: Object as () => WorkflowNode | null, default: null },
type: Object as () => WorkflowNode | null, nextNode: { type: Object as () => WorkflowNode | null, default: null },
default: null beforeNode: { type: Object as () => WorkflowNode | null, default: null },
}, dataId: { type: String, default: '' },
dataId: { deployId: { type: String, default: '' },
type: String, taskId: { type: String, default: '' },
default: '' procInsId: { type: String, default: '' },
}, externalFormData: { type: Object as () => Record<string, any>, default: () => ({}) },
deployId: { formData: { type: Object as () => Record<string, any>, default: () => ({}) },
type: String, disabled: { type: Boolean, default: false },
default: '' assignee: { type: String, default: '' },
}, userType: { type: String, default: '' },
taskId: { showApprovalPanel: { type: Boolean, default: false }
type: String,
default: ''
},
procInsId: {
type: String,
default: ''
},
externalFormData: {
type: Object as () => Record<string, any>,
default: () => ({})
},
formData: {
type: Object as () => Record<string, any>,
default: () => ({})
},
disabled: {
type: Boolean,
default: false
},
assignee: {
type: String,
default: ''
},
userType: {
type: String,
default: ''
},
showApprovalPanel: {
type: Boolean,
default: false
}
}) })
const emit = defineEmits(['update:form-data', 'form-mounted','approval-success', 'approval-fail'])
const componentCache = new Map()
const modules = import.meta.glob('@/views/**/*.vue')
const formComponentRef = ref<FormComponentInstance | null>(null)
const formData = ref<any>({})
function handleApproval() { function handleApproval() {
openModal(true, { openModal(true, {
deployId: props.deployId,
dataId: props.dataId,
userType: props.userType,
assignee: props.assignee,
beforeFlowNode: props.beforeNode,
isUpdate: false, isUpdate: false,
showFooter: true, showFooter: true,
taskType: 'approval', taskType: 'approval'
}); });
} }
function handleSend() { function handleSend() {
taskOpenModal(true, { taskOpenModal(true, {
taskTitle: '发送', taskTitle: '发送', isUpdate: false, showFooter: true, taskType: 'send',
isUpdate: false, deployId: props.deployId, taskId: props.taskId, procInsId: props.procInsId, dataId: props.dataId, assignee: props.assignee, userType: props.userType,
showFooter: true,
taskType: 'send',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
assignee: props.assignee,
userType: props.userType,
}); });
} }
function handleReject() { function handleReject() {
taskOpenModal(true, { taskOpenModal(true, {
taskTitle: '驳回', taskTitle: '驳回', isUpdate: false, showFooter: true, taskType: 'reject',
isUpdate: false, deployId: props.deployId, taskId: props.taskId, procInsId: props.procInsId, dataId: props.dataId, assignee: props.assignee, userType: props.userType,
showFooter: true,
taskType: 'reject',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
assignee: props.assignee,
userType: props.userType,
}); });
} }
function handleTransmit() { function handleTransmit() {
taskOpenModal(true, { taskOpenModal(true, {
taskTitle: '转办', taskTitle: '转办', isUpdate: false, showFooter: true, taskType: 'transmit',
isUpdate: false, deployId: props.deployId, taskId: props.taskId, procInsId: props.procInsId, dataId: props.dataId,
showFooter: true,
taskType: 'transmit',
deployId: props.deployId,
taskId: props.taskId,
procInsId: props.procInsId,
dataId: props.dataId,
}); });
} }
function handleRead() { function handleRead() {
taskOpenModal(true, { taskOpenModal(true, {
taskTitle: '转阅', taskTitle: '转阅', isUpdate: false, showFooter: true, taskType: 'read',
isUpdate: false, deployId: props.deployId, taskId: props.taskId, procInsId: props.procInsId, dataId: props.dataId, assignee: props.assignee, userType: props.userType,
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 modules = import.meta.glob('@/views/**/*.vue')
const formComponentRef = ref<FormComponentInstance | null>(null)
const formData = ref<any>({})
const isEditStatus = ref(false);
function setFormRef(el: any, nodeId: string) { function setFormRef(el: any, nodeId: string) {
if (el) { if (el) {
formComponentRef.value = el formComponentRef.value = el
...@@ -193,261 +131,113 @@ function setFormRef(el: any, nodeId: string) { ...@@ -193,261 +131,113 @@ function setFormRef(el: any, nodeId: string) {
} }
} }
// 调用 initFormData
async function callInitFormData(formComponent: FormComponentInstance) { async function callInitFormData(formComponent: FormComponentInstance) {
if (!props.dataId) return if (!props.dataId || !formComponent?.initFormData) return
if (!formComponent) return try { await formComponent.initFormData(props.dataId) } catch (error) { console.error('initFormData 调用失败:', error) }
if (typeof formComponent.initFormData === 'function') {
try {
await formComponent.initFormData(props.dataId)
} catch (error) {
console.error('initFormData 调用失败:', error)
}
}
} }
// 处理表单数据更新
async function handleFormDataUpdate(data: any) { async function handleFormDataUpdate(data: any) {
const curFormData = await getFormData(); const curFormData = await getFormData()
formData.value = { ...curFormData, ...data } formData.value = { ...curFormData, ...data }
await handleSaveCurForm(curFormData.value) formData.value["bpmNodeId"] = props.nextNode?.id
if (formComponentRef.value?.saveForm){
await formComponentRef.value.saveForm(formData.value);
}
emit('update:form-data', formData.value) emit('update:form-data', formData.value)
} }
// 获取当前表单实例 async function updateApprovalSuccess(data: any) {
function getFormInstance(): FormComponentInstance | null { emit('approval-success', data.dataId)
return formComponentRef.value }
async function updateApprovalFail(data: any) {
emit('approval-fail', data.dataId)
} }
// 获取表单数据
function getFormInstance(): FormComponentInstance | null { return formComponentRef.value }
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 { return await formComponent.getFormData() } catch (error) { console.error('调用 getFormData 失败:', error) }
const data = await formComponent.getFormData()
return data
} catch (error) {
console.error('调用 getFormData 失败:', error)
}
}
if (formComponent.formData !== undefined) {
return formComponent.formData
} }
if (formComponent.formData !== undefined) return formComponent.formData
return formData.value return formData.value
} }
// 验证表单
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(); return true } catch (error) { return false }
await formComponent.validate()
return true
} catch (error) {
return false
}
} }
return true return true
} }
// 重置表单数据
function resetFormData() { function resetFormData() {
if (props.editableNode && props.externalFormData[props.editableNode.id]) { formData.value = props.editableNode && props.externalFormData[props.editableNode.id] ? { ...props.externalFormData[props.editableNode.id] } : {}
formData.value = { ...props.externalFormData[props.editableNode.id] }
} else {
formData.value = {}
}
} }
// 重新加载表单数据
async function reloadFormData() { async function reloadFormData() {
if (formComponentRef.value && typeof formComponentRef.value.initFormData === 'function') { if (formComponentRef.value?.initFormData) await formComponentRef.value.initFormData(props.dataId)
await formComponentRef.value.initFormData(props.dataId)
}
} }
// 监听 dataId 变化
watch(() => props.dataId, async () => { watch(() => props.dataId, async () => {
if (formComponentRef.value) { if (formComponentRef.value) { await nextTick(); await reloadFormData() }
await nextTick()
await reloadFormData()
}
}) })
// 监听外部数据变化
watch(() => props.externalFormData, (newData) => { watch(() => props.externalFormData, (newData) => {
if (newData && props.editableNode && newData[props.editableNode.id]) { if (newData && props.editableNode && newData[props.editableNode.id]) formData.value = newData[props.editableNode.id]
formData.value = newData[props.editableNode.id]
}
}, { deep: true }) }, { deep: true })
// 监听可编辑节点变化 watch(() => props.editableNode, () => { resetFormData() })
watch(() => props.editableNode, () => {
resetFormData()
})
function getComponent(url: string) { function getComponent(url: string) {
if (!url) { if (!url) return createEmptyComponent()
return createEmptyComponent() if (componentCache.has(url)) return componentCache.get(url)
} let componentPath = url.includes('/views') ? `/src${url}` : `/src/views${url}`
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) componentPath += '.vue'
if (componentCache.has(url)) {
return componentCache.get(url)
}
let componentPath = ''
if (url.includes('/views')) {
componentPath = `/src${url}`
} else {
componentPath = `/src/views${url}`
}
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
componentPath += '.vue'
}
const loader = modules[componentPath] const loader = modules[componentPath]
if (!loader) { if (!loader) {
console.error('未找到组件:', componentPath)
const ErrorComponent = createErrorComponent(`组件未找到: ${componentPath}`) const ErrorComponent = createErrorComponent(`组件未找到: ${componentPath}`)
componentCache.set(url, ErrorComponent) componentCache.set(url, ErrorComponent)
return ErrorComponent return ErrorComponent
} }
const AsyncComponent = defineAsyncComponent({ const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>, loader: () => loader() as Promise<{ default: any }>,
loadingComponent: { loadingComponent: { render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...') },
render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...') errorComponent: { render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败') },
}, delay: 200, timeout: 3000
errorComponent: {
render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败')
},
delay: 200,
timeout: 3000
}) })
componentCache.set(url, AsyncComponent) componentCache.set(url, AsyncComponent)
return AsyncComponent return AsyncComponent
} }
function createEmptyComponent() { function createEmptyComponent() {
return { return { render: () => h('div', { style: 'color: #999; padding: 20px; text-align: center;' }, '该节点未配置表单') }
render: () => h('div', { style: 'color: #999; padding: 20px; text-align: center;' }, '该节点未配置表单')
}
} }
function createErrorComponent(msg: string) { function createErrorComponent(msg: string) {
return { return { render: () => h('div', { style: 'color: red; padding: 20px;' }, msg) }
render: () => h('div', { style: 'color: red; padding: 20px;' }, msg)
}
} }
async function handleSaveCurForm(curFormData: any) {
const handleSaveCurForm = async (curFormData) => { //const valid = await validateForm()
const valid = await validateForm(); // if (!valid || !formComponentRef.value?.saveForm) return
if (!valid) {
return
}
if (formComponentRef.value&&typeof formComponentRef.value.saveForm === 'function') {
await formComponentRef.value.saveForm(curFormData) await formComponentRef.value.saveForm(curFormData)
}
} }
defineExpose({ defineExpose({ getFormData, validateForm, getFormInstance, resetFormData, reloadFormData })
getFormData,
validateForm,
getFormInstance,
resetFormData,
reloadFormData
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.form-panel { .form-panel {
flex: 1; flex: 1; display: flex; flex-direction: column; overflow: hidden; background-color: #f5f7fa;
display: flex; .form-card { height: 100%; display: flex; flex-direction: column; background-color: #fff; :deep(.ant-card-head) { padding: 0 24px; background-color: #e9ecef; border-bottom: 1px solid #e8eef2; min-height: auto; .ant-card-head-wrapper { height: auto; } } :deep(.ant-card-body) { flex: 1; padding: 0; overflow: hidden; display: flex; flex-direction: column; } }
flex-direction: column; .form-header { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 16px 0; .form-title { font-size: 16px; font-weight: 500; color: #1f2f3d; } }
overflow: hidden; .form-content { flex: 1; overflow-y: auto; padding: 24px; &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px; } &::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 3px; &:hover { background: #a8a8a8; } } }
background-color: #f5f7fa; .empty-form-state { flex: 1; display: flex; align-items: center; justify-content: center; }
.form-card {
height: 100%;
display: flex;
flex-direction: column;
background-color: #fff;
:deep(.ant-card-head) {
padding: 0 24px;
background-color: #e9ecef;
border-bottom: 1px solid #e8eef2;
min-height: auto;
.ant-card-head-wrapper {
height: auto;
}
}
:deep(.ant-card-body) {
flex: 1;
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
}
.form-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 16px 0;
.form-title {
font-size: 16px;
font-weight: 500;
color: #1f2f3d;
}
}
.form-content {
flex: 1;
overflow-y: auto;
padding: 24px;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 3px;
&:hover {
background: #a8a8a8;
}
}
}
.empty-form-state {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
} }
</style> </style>
<template> <template>
<BasicDrawer v-bind="$attrs" title="流程追踪" width="100%" <BasicDrawer v-bind="$attrs" title="流程追踪" width="100%" @register="registerBasicDrawer" :header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }">
@register="registerBasicDrawer"
:header-style="{ backgroundColor: '#018ffb', borderBottom: '1px solid #e8eef2' }">
<div class="drawer-content"> <div class="drawer-content">
<a-tabs v-model:activeKey="activeKey" type="card" @change="handleTabChange"> <a-tabs v-model:activeKey="activeKey" type="card" @change="handleTabChange">
<a-tab-pane key="chart" tab="流程图"> <a-tab-pane key="chart" tab="流程图">
...@@ -21,45 +19,30 @@ ...@@ -21,45 +19,30 @@
import FlowHistoryChart from './FlowHistoryChart.vue'; import FlowHistoryChart from './FlowHistoryChart.vue';
import FlowHistoryRecord from './FlowHistoryRecord.vue'; import FlowHistoryRecord from './FlowHistoryRecord.vue';
const activeKey = ref('chart'); const activeKey = ref('chart');
const procInsId = ref('');
const dataId = ref('');
const deployId = ref('');
const refFlowHistoryChart = ref(); const refFlowHistoryChart = ref();
const refFlowHistoryRecord = ref(); const refFlowHistoryRecord = ref();
const curData = ref({}); const curData = ref({});
const callback = (data) => { const callback = (data) => {
curData.value = data; curData.value = data;
handleTabChange('chart'); handleTabChange('chart');
} }
const handleTabChange = (key) => { const handleTabChange = (key) => {
if (key === 'chart') { if (key === 'chart') {
nextTick(() => { nextTick(() => refFlowHistoryChart.value?.loadData(curData.value));
refFlowHistoryChart.value.loadData(curData.value);
})
} else if (key === 'record') { } else if (key === 'record') {
nextTick(() => { nextTick(() => refFlowHistoryRecord.value?.loadData(curData.value));
refFlowHistoryRecord.value.loadData(curData.value);
})
} }
activeKey.value = key; activeKey.value = key;
} }
const [registerBasicDrawer, { closeDrawer, setDrawerProps }] = useDrawerInner(callback);
const [registerBasicDrawer, { closeDrawer }] = useDrawerInner(callback);
</script> </script>
<style scoped> <style scoped>
.drawer-content { .drawer-content { height: 100%; }
height: 100%; .drawer-content :deep(.ant-tabs) { height: 100%; }
} .drawer-content :deep(.ant-tabs-content) { height: calc(100% - 40px); overflow-y: auto; }
.drawer-content :deep(.ant-tabs) {
height: 100%;
}
.drawer-content :deep(.ant-tabs-content) {
height: calc(100% - 40px);
overflow-y: auto;
}
</style> </style>
...@@ -23,22 +23,12 @@ ...@@ -23,22 +23,12 @@
<clock-circle-outlined v-else /> <clock-circle-outlined v-else />
</div> </div>
</template> </template>
<a-card <a-card :title="node.name" :bordered="false" size="small" class="history-node-card" :class="{ 'last-card': index === readonlyNodes.length - 1 }">
:title="node.name"
:bordered="false"
size="small"
class="history-node-card"
:class="{ 'last-card': index === readonlyNodes.length - 1 }"
>
<template #extra> <template #extra>
<a-button type="link" size="small" @click="togglePreview(node.id)"> <a-button type="link" size="small" @click="togglePreview(node.id)">
{{ expandedPreviewId === node.id ? '收起' : '展开' }} {{ expandedPreviewId === node.id ? '收起' : '展开' }}
</a-button> </a-button>
<!-- <a-tag :color="index === readonlyNodes.length - 1 ? 'orange' : 'green'" size="small">
{{ index === readonlyNodes.length - 1 ? '已通过' : '已完成' }}
</a-tag> -->
</template> </template>
<!-- 显示历史表单数据 -->
<template v-if="showHistoryFormData"> <template v-if="showHistoryFormData">
<div class="history-form-preview"> <div class="history-form-preview">
<div v-show="expandedPreviewId === node.id" class="form-preview-content"> <div v-show="expandedPreviewId === node.id" class="form-preview-content">
...@@ -76,13 +66,9 @@ interface WorkflowNode { ...@@ -76,13 +66,9 @@ interface WorkflowNode {
formListUrl?: string formListUrl?: string
procDefId?: string procDefId?: string
dataId?: string dataId?: string
processTime?: string
processor?: string
comment?: string
[key: string]: any [key: string]: any
} }
// 表单组件实例类型
interface FormComponentInstance extends ComponentPublicInstance { interface FormComponentInstance extends ComponentPublicInstance {
validate?: () => Promise<any> validate?: () => Promise<any>
getFormData?: () => any getFormData?: () => any
...@@ -92,210 +78,107 @@ interface FormComponentInstance extends ComponentPublicInstance { ...@@ -92,210 +78,107 @@ interface FormComponentInstance extends ComponentPublicInstance {
} }
const props = defineProps({ const props = defineProps({
readonlyNodes: { readonlyNodes: { type: Array as () => WorkflowNode[], required: true, default: () => [] },
type: Array as () => WorkflowNode[], dataId: { type: String, default: '' },
required: true, showHistoryFormData: { type: Boolean, default: true }
default: () => []
},
dataId: {
type: String,
default: ''
},
showHistoryFormData: {
type: Boolean,
default: true
}
}) })
// 简单空状态图片
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE
// 组件缓存
const componentCache = new Map<string, any>() const componentCache = new Map<string, any>()
// 表单实例缓存,用于调用 initFormData
const formInstanceCache = new Map<string, FormComponentInstance>() const formInstanceCache = new Map<string, FormComponentInstance>()
// 动态导入所有表单组件
const modules = import.meta.glob('@/views/**/*.vue') const modules = import.meta.glob('@/views/**/*.vue')
// 历史表单预览展开状态
const expandedPreviewId = ref<string>('') const expandedPreviewId = ref<string>('')
// 切换预览展开/收起
function togglePreview(nodeId: string) { function togglePreview(nodeId: string) {
expandedPreviewId.value = expandedPreviewId.value === nodeId ? '' : nodeId expandedPreviewId.value = expandedPreviewId.value === nodeId ? '' : nodeId
} }
// 设置表单组件 ref
function setFormRef(el: any, nodeId: string) { function setFormRef(el: any, nodeId: string) {
if (el) { if (el) {
formInstanceCache.set(nodeId, el) formInstanceCache.set(nodeId, el)
// 组件挂载后调用 initFormData 加载数据
callInitFormData(el, nodeId) callInitFormData(el, nodeId)
} }
} }
// 调用 initFormData 加载表单数据
async function callInitFormData(formComponent: FormComponentInstance, nodeId: string) { async function callInitFormData(formComponent: FormComponentInstance, nodeId: string) {
if (!props.dataId) { if (!props.dataId || !formComponent) return
console.warn('dataId 为空,无法加载表单数据')
return
}
if (!formComponent) return
// 等待一下确保组件完全挂载
await nextTick() await nextTick()
// 调用表单组件的 initFormData 方法
if (typeof formComponent.initFormData === 'function') { if (typeof formComponent.initFormData === 'function') {
try { try {
console.log(`加载历史表单数据: nodeId=${nodeId}, dataId=${props.dataId}`)
await formComponent.initFormData(props.dataId) await formComponent.initFormData(props.dataId)
} catch (error) { } catch (error) {
console.error('initFormData 调用失败:', error) console.error('initFormData 调用失败:', error)
} }
} else {
console.warn('表单组件没有 initFormData 方法', formComponent)
} }
} }
// 解析组件路径
function resolveComponentPath(node: WorkflowNode): string | null { function resolveComponentPath(node: WorkflowNode): string | null {
// 优先使用 formUrl let url = node.formUrl || node.formListUrl
let url = node.formUrl if (!url) return null
let componentPath = url.includes('/src/') ? url : url.startsWith('/views') ? `/src${url}` : `/src/views/${url.replace(/^\.\.?\//, '')}`
if (!url) { if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) componentPath += '.vue'
// 如果没有 formUrl,尝试从 formListUrl 推断
url = node.formListUrl
}
if (!url) {
return null
}
// 处理不同的路径格式
let componentPath = ''
// 如果已经是完整路径
if (url.includes('/src/')) {
componentPath = url
}
// 如果以 /views 开头
else if (url.startsWith('/views')) {
componentPath = `/src${url}`
}
// 如果是相对路径
else if (url.startsWith('./') || url.startsWith('../')) {
componentPath = `/src/views/${url.replace(/^\.\.?\//, '')}`
}
// 如果只是文件名或简单路径
else {
componentPath = `/src/views/${url}`
}
// 确保有 .vue 后缀
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
componentPath += '.vue'
}
return componentPath return componentPath
} }
// 根据节点获取组件
function getComponent(node: WorkflowNode) { function getComponent(node: WorkflowNode) {
const cacheKey = node.formUrl || node.formListUrl || node.id const cacheKey = node.formUrl || node.formListUrl || node.id
if (componentCache.has(cacheKey)) return componentCache.get(cacheKey)
if (componentCache.has(cacheKey)) {
return componentCache.get(cacheKey)
}
const componentPath = resolveComponentPath(node) const componentPath = resolveComponentPath(node)
if (!componentPath) { if (!componentPath) {
const EmptyComponent = createEmptyComponent() const EmptyComponent = createEmptyComponent()
componentCache.set(cacheKey, markRaw(EmptyComponent)) componentCache.set(cacheKey, markRaw(EmptyComponent))
return EmptyComponent return EmptyComponent
} }
// 尝试在所有模块中查找
let loader = null let loader = null
// 精确匹配
for (const [path, moduleLoader] of Object.entries(modules)) { for (const [path, moduleLoader] of Object.entries(modules)) {
if (path === componentPath || path.endsWith(componentPath.split('/').pop() || '')) { if (path === componentPath || path.endsWith(componentPath.split('/').pop() || '')) {
loader = moduleLoader loader = moduleLoader
break break
} }
} }
// 如果没找到,尝试模糊匹配(去掉路径前缀)
if (!loader) { if (!loader) {
const fileName = componentPath.split('/').pop()?.replace(/\.vue$/, '') || '' const fileName = componentPath.split('/').pop()?.replace(/\.vue$/, '') || ''
for (const [path, moduleLoader] of Object.entries(modules)) { for (const [path, moduleLoader] of Object.entries(modules)) {
if (path.includes(fileName)) { if (path.includes(fileName)) { loader = moduleLoader; break }
loader = moduleLoader
break
}
} }
} }
if (!loader) { if (!loader) {
console.warn('未找到表单组件:', componentPath, '节点:', node.name)
const NotFoundComponent = createNotFoundComponent(`表单组件未找到: ${componentPath}`) const NotFoundComponent = createNotFoundComponent(`表单组件未找到: ${componentPath}`)
componentCache.set(cacheKey, markRaw(NotFoundComponent)) componentCache.set(cacheKey, markRaw(NotFoundComponent))
return NotFoundComponent return NotFoundComponent
} }
const AsyncComponent = defineAsyncComponent({ const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>, loader: () => loader() as Promise<{ default: any }>,
loadingComponent: { loadingComponent: { render: () => h('div', { class: 'component-loading' }, '加载中...') },
render: () => h('div', { class: 'component-loading' }, '加载中...') errorComponent: { render: (props: any) => h('div', { class: 'component-error' }, props.error?.message || '组件加载失败') },
}, delay: 200, timeout: 10000
errorComponent: {
render: (props: any) => h('div', { class: 'component-error' },
props.error?.message || '组件加载失败')
},
delay: 200,
timeout: 10000
}) })
componentCache.set(cacheKey, markRaw(AsyncComponent)) componentCache.set(cacheKey, markRaw(AsyncComponent))
return AsyncComponent return AsyncComponent
} }
// 创建空组件(未配置表单)
function createEmptyComponent() { function createEmptyComponent() {
return { return { name: 'EmptyFormComponent', render: () => h('div', { class: 'empty-form-message' }, '该节点未配置表单') }
name: 'EmptyFormComponent',
render: () => h('div', { class: 'empty-form-message' }, '该节点未配置表单')
}
} }
// 创建未找到组件
function createNotFoundComponent(msg: string) { function createNotFoundComponent(msg: string) {
return { return { name: 'NotFoundComponent', render: () => h('div', { class: 'not-found-message' }, msg) }
name: 'NotFoundComponent',
render: () => h('div', { class: 'not-found-message' }, msg)
}
} }
// 监听 dataId 变化,重新加载所有已展开的历史表单数据
watch(() => props.dataId, async () => { watch(() => props.dataId, async () => {
if (expandedPreviewId.value && formInstanceCache.has(expandedPreviewId.value)) { if (expandedPreviewId.value && formInstanceCache.has(expandedPreviewId.value)) {
const formComponent = formInstanceCache.get(expandedPreviewId.value) const formComponent = formInstanceCache.get(expandedPreviewId.value)
if (formComponent && typeof formComponent.initFormData === 'function') { if (formComponent?.initFormData) {
await nextTick() await nextTick()
await formComponent.initFormData(props.dataId) await formComponent.initFormData(props.dataId)
} }
} }
}) })
// 监听展开状态变化,当展开时重新加载数据 watch(expandedPreviewId, async (newId) => {
watch(expandedPreviewId, async (newId, oldId) => {
if (newId && formInstanceCache.has(newId)) { if (newId && formInstanceCache.has(newId)) {
const formComponent = formInstanceCache.get(newId) const formComponent = formInstanceCache.get(newId)
if (formComponent && typeof formComponent.initFormData === 'function') { if (formComponent?.initFormData) {
await nextTick() await nextTick()
await formComponent.initFormData(props.dataId) await formComponent.initFormData(props.dataId)
} }
...@@ -311,207 +194,40 @@ watch(expandedPreviewId, async (newId, oldId) => { ...@@ -311,207 +194,40 @@ watch(expandedPreviewId, async (newId, oldId) => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden; overflow: hidden;
.history-card-container { .history-card-container {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-color: #f5f7fa; background-color: #f5f7fa;
:deep(.ant-card-head) { padding: 0 20px; background-color: #e9ecef; border-bottom: 1px solid #e8eef2; min-height: auto; .ant-card-head-wrapper { height: auto; } }
:deep(.ant-card-head) { :deep(.ant-card-body) { flex: 1; overflow: hidden; }
padding: 0 20px;
background-color: #e9ecef; // 灰色背景,比整体背景稍深
border-bottom: 1px solid #e8eef2;
min-height: auto;
.ant-card-head-wrapper {
height: auto;
}
}
:deep(.ant-card-body) {
flex: 1;
overflow: hidden;
}
} }
.history-header { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 16px 0; .history-title { font-size: 16px; font-weight: 500; color: #1f2f3d; } }
.history-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 16px 0;
.history-title {
font-size: 16px;
font-weight: 500;
color: #1f2f3d;
}
}
.history-content { .history-content {
flex: 1; flex: 1; overflow-y: auto; padding: 16px 12px;
overflow-y: auto; &.empty-history { display: flex; align-items: center; justify-content: center; }
padding: 16px 12px; &.has-scroll { padding-right: 8px; }
&::-webkit-scrollbar { width: 4px; }
&.empty-history { &::-webkit-scrollbar-track { background: #e8eef2; border-radius: 2px; }
display: flex; &::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 2px; &:hover { background: #a8a8a8; } }
align-items: center; }
justify-content: center; .empty-history-state { display: flex; align-items: center; justify-content: center; height: 100%; }
} :deep(.ant-timeline) { .ant-timeline-item { padding-bottom: 20px; } .ant-timeline-item-tail { border-left-color: #d9d9d9; } }
.timeline-dot { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; background-color: #fff; border-radius: 50%; font-size: 14px; .anticon { font-size: 14px; } }
&.has-scroll {
padding-right: 8px;
}
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: #e8eef2;
border-radius: 2px;
}
&::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 2px;
&:hover {
background: #a8a8a8;
}
}
}
.empty-history-state {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
:deep(.ant-timeline) {
.ant-timeline-item {
padding-bottom: 20px;
}
.ant-timeline-item-tail {
border-left-color: #d9d9d9;
}
}
.timeline-dot {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 50%;
font-size: 14px;
.anticon {
font-size: 14px;
}
}
.history-node-card { .history-node-card {
background-color: #fff; background-color: #fff;
border-radius: 8px; border-radius: 8px;
margin-bottom: 8px; margin-bottom: 8px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
transition: box-shadow 0.2s; transition: box-shadow 0.2s;
&:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); }
&:hover { &.last-card { border-left: 3px solid #1890ff; }
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); :deep(.ant-card-head) { padding: 12px 16px; min-height: auto; border-bottom: 1px solid #f0f0f0; .ant-card-head-title { font-size: 14px; font-weight: 500; padding: 0; } .ant-card-extra { padding: 0; } }
} :deep(.ant-card-body) { padding: 12px 16px; }
}
&.last-card { .history-form-preview { margin-top: 8px; .preview-header { display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #8c8c8c; } .form-preview-content { margin-top: 12px; background: #fafafa; border-radius: 4px; padding: 12px; } }
border-left: 3px solid #1890ff; .history-form-component { :deep(*) { font-size: 12px; } }
} .component-loading, .component-error, .empty-form-message, .not-found-message { text-align: center; padding: 20px; color: #999; }
.component-error, .not-found-message { color: #ff4d4f; }
:deep(.ant-card-head) {
padding: 12px 16px;
min-height: auto;
border-bottom: 1px solid #f0f0f0;
.ant-card-head-title {
font-size: 14px;
font-weight: 500;
padding: 0;
}
.ant-card-extra {
padding: 0;
}
}
:deep(.ant-card-body) {
padding: 12px 16px;
}
}
.history-card-content {
.node-info {
display: flex;
margin-bottom: 8px;
font-size: 13px;
&:last-child {
margin-bottom: 0;
}
.node-label {
width: 70px;
color: #8c8c8c;
flex-shrink: 0;
}
.node-value {
color: #262626;
flex: 1;
word-break: break-all;
}
}
}
.history-form-preview {
margin-top: 8px;
.preview-header {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
color: #8c8c8c;
}
.form-preview-content {
margin-top: 12px;
background: #fafafa;
border-radius: 4px;
padding: 12px;
}
}
.history-form-component {
:deep(*) {
font-size: 12px;
}
}
.component-loading,
.component-error,
.empty-form-message,
.not-found-message {
text-align: center;
padding: 20px;
color: #999;
}
.component-error,
.not-found-message {
color: #ff4d4f;
}
} }
</style> </style>
...@@ -14,20 +14,11 @@ ...@@ -14,20 +14,11 @@
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item label="用户类型" required> <a-form-item label="用户类型" required>
<a-radio-group v-model:value="localUserType" disabled> <a-radio-group v-model:value="localUserType" disabled>
<a-radio value="user"> <a-radio value="user"><UserOutlined /> 用户</a-radio>
<UserOutlined /> 用户 <a-radio value="role"><TeamOutlined /> 角色</a-radio>
</a-radio>
<a-radio value="role">
<TeamOutlined /> 角色
</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item v-if="!assigneeId" :label="localUserType === 'user' ? '选择用户' : '选择角色'">
<!-- 用户/角色选择区域(未选择时) -->
<a-form-item
v-if="!assigneeId"
:label="localUserType === 'user' ? '选择用户' : '选择角色'"
>
<div class="assignee-selector"> <div class="assignee-selector">
<a-input <a-input
:value="assigneeDisplayName" :value="assigneeDisplayName"
...@@ -41,8 +32,6 @@ ...@@ -41,8 +32,6 @@
</a-input> </a-input>
</div> </div>
</a-form-item> </a-form-item>
<!-- 已指定信息展示 -->
<div v-if="assigneeId" 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>
...@@ -53,12 +42,9 @@ ...@@ -53,12 +42,9 @@
{{ assigneeDisplayName || '--' }} {{ assigneeDisplayName || '--' }}
</a-descriptions-item> </a-descriptions-item>
</a-descriptions> </a-descriptions>
<a-button type="link" size="small" @click="clearAssignee" class="change-btn"> <a-button type="link" size="small" @click="clearAssignee" class="change-btn">重新选择</a-button>
重新选择
</a-button>
</div> </div>
</a-form> </a-form>
<div class="assignee-actions"> <div class="assignee-actions">
<a-space> <a-space>
<a-button @click="handleClose">取消</a-button> <a-button @click="handleClose">取消</a-button>
...@@ -68,27 +54,13 @@ ...@@ -68,27 +54,13 @@
</a-card> </a-card>
</div> </div>
<RoleSelectModal <RoleSelectModal rowKey="id" @register="registerSelRoleModal" @getSelectResult="onSelectRoleOk" isRadioSelection :showButton="false" labelKey="roleName" />
rowKey="id" <UserSelectModal rowKey="id" @register="registerSelUserModal" @getSelectResult="onSelectUserOk" isRadioSelection :showButton="false" labelKey="realname" />
@register="registerSelRoleModal"
@getSelectResult="onSelectRoleOk"
isRadioSelection
:showButton="false"
labelKey="roleName"
/>
<UserSelectModal
rowKey="id"
@register="registerSelUserModal"
@getSelectResult="onSelectUserOk"
isRadioSelection
:showButton="false"
labelKey="realname"
/>
</BasicDrawer> </BasicDrawer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch, nextTick } from 'vue' import { ref } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { useModal } from '/@/components/Modal' import { useModal } from '/@/components/Modal'
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer' import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'
...@@ -97,43 +69,26 @@ import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserS ...@@ -97,43 +69,26 @@ import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserS
import RoleSelectModal from '/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue' import RoleSelectModal from '/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue'
import { complete, getMyTaskFlow } from '/@/components/Process/api/todo' import { complete, getMyTaskFlow } from '/@/components/Process/api/todo'
interface formDadta {
assignee: string
userType: string
deployId?: string
required?: string
assigneeName?: string
dataId?: string
}
const emit = defineEmits(['success', 'error', 'close']) const emit = defineEmits(['success', 'error', 'close'])
const [registerSelUserModal, { openModal: userOpenModal }] = useModal() const [registerSelUserModal, { openModal: userOpenModal }] = useModal()
const [registerSelRoleModal, { openModal: roleOpenModal }] = useModal() const [registerSelRoleModal, { openModal: roleOpenModal }] = useModal()
// 状态
const localUserType = ref<'user' | 'role'>('user') const localUserType = ref<'user' | 'role'>('user')
const assigneeId = ref('') const assigneeId = ref('')
const assigneeDisplayName = ref('') 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 接收外部传入的数据 const [registerBasicDrawer, { closeDrawer }] = 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 || ''
if (data.assignee) { if (data.assignee) {
localUserType.value = data.userType === 'role' ? 'role' : 'user' localUserType.value = data.userType === 'role' ? 'role' : 'user'
assigneeId.value = data.assignee assigneeId.value = data.assignee
if (data.assigneeName) { assigneeDisplayName.value = data.assigneeName || ''
assigneeDisplayName.value = data.assigneeName
}
} else { } else {
localUserType.value = data.userType === 'role' ? 'role' : 'user' localUserType.value = data.userType === 'role' ? 'role' : 'user'
} }
...@@ -146,14 +101,9 @@ const clearAssignee = () => { ...@@ -146,14 +101,9 @@ const clearAssignee = () => {
} }
const handleSelect = () => { const handleSelect = () => {
if (localUserType.value === 'user') { localUserType.value === 'user' ? userOpenModal() : roleOpenModal()
userOpenModal()
} else {
roleOpenModal()
}
} }
// 选择用户回调
const onSelectUserOk = (options: any[], values: any[]) => { const onSelectUserOk = (options: any[], values: any[]) => {
if (!values?.length) return if (!values?.length) return
assigneeId.value = values[0] assigneeId.value = values[0]
...@@ -161,7 +111,6 @@ const onSelectUserOk = (options: any[], values: any[]) => { ...@@ -161,7 +111,6 @@ const onSelectUserOk = (options: any[], values: any[]) => {
localUserType.value = 'user' localUserType.value = 'user'
} }
// 选择角色回调
const onSelectRoleOk = (options: any[], values: any[]) => { const onSelectRoleOk = (options: any[], values: any[]) => {
if (!values?.length) return if (!values?.length) return
assigneeId.value = values[0] assigneeId.value = values[0]
...@@ -175,7 +124,6 @@ const handleClose = () => { ...@@ -175,7 +124,6 @@ const handleClose = () => {
} }
const handleConfirm = async () => { const handleConfirm = async () => {
confirmLoading.value = true confirmLoading.value = true
try { try {
if (dataId.value && deployId.value) { if (dataId.value && deployId.value) {
...@@ -187,28 +135,20 @@ const handleConfirm = async () => { ...@@ -187,28 +135,20 @@ const handleConfirm = async () => {
taskId: myTaskFlow.taskId, taskId: myTaskFlow.taskId,
dataId: dataId.value, dataId: dataId.value,
comment: '', comment: '',
values: { values: { approval: assigneeId.value, approvalType: localUserType.value },
approval: assigneeId.value,
approvalType: localUserType.value
},
}) })
} }
} }
emit('success', dataId.value) emit('success', dataId.value)
nextTick(() => {
message.success('任务发送成功') message.success('任务发送成功')
handleClose() handleClose()
}) } catch (error) {
} catch (error: any) {
emit('error', error) emit('error', error)
} finally { } finally {
confirmLoading.value = false confirmLoading.value = false
} }
} }
// 对外暴露的方法
const getAssigneeData = () => ({ const getAssigneeData = () => ({
userType: localUserType.value, userType: localUserType.value,
assignee: assigneeId.value, assignee: assigneeId.value,
...@@ -228,110 +168,13 @@ const setRoleInfo = (roleId: string, roleNameValue: string) => { ...@@ -228,110 +168,13 @@ const setRoleInfo = (roleId: string, roleNameValue: string) => {
localUserType.value = 'role' localUserType.value = 'role'
} }
defineExpose({ defineExpose({ getAssigneeData, submit: handleConfirm, setUserInfo, setRoleInfo })
getAssigneeData,
submit: handleConfirm,
setUserInfo,
setRoleInfo
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.drawer-content { .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; } } }
height: 100%; .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; } }
overflow-y: auto; .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; } } } }
padding: 16px 20px; .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; }
&::-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> </style>
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
> >
<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" v-if="showHistoryVisible"> <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"
...@@ -20,12 +19,12 @@ ...@@ -20,12 +19,12 @@
:show-history-form-data="props.showHistoryFormData" :show-history-form-data="props.showHistoryFormData"
/> />
</a-col> </a-col>
<!-- 中间表单区域 -->
<a-col :width="props.centerPanelWidth"> <a-col :width="props.centerPanelWidth">
<CurrentFormPanel style="width: 100%;height: 100%;" <CurrentFormPanel style="width: 100%;height: 100%;"
ref="currentFormPanelRef" ref="currentFormPanelRef"
:editable-node="editableNode" :editable-node="editableNode"
:next-node="workflowNodes[currentNodeIndex + 1]"
:before-node="workflowNodes[currentNodeIndex - 1]"
:data-id="dataId" :data-id="dataId"
:external-form-data="externalFormData" :external-form-data="externalFormData"
:form-data="props.formData" :form-data="props.formData"
...@@ -39,26 +38,10 @@ ...@@ -39,26 +38,10 @@
:show-approval-panel="props.showApprovalPanel" :show-approval-panel="props.showApprovalPanel"
@update:form-data="handleFormDataUpdate" @update:form-data="handleFormDataUpdate"
@form-mounted="handleFormMounted" @form-mounted="handleFormMounted"
@approval-success="handleApprovalSuccess"
/>
</a-col>
<!-- 右侧审核面板 -->
<!-- <a-col v-if="showApprovalPanel" :width="props.rightPanelWidth">
<ApprovalPanel style="width: 100%;height: 100%;"
ref="approvalPanelRef"
:current-node="editableNode"
:before-flow-node="workflowNodes[props.currentNodeIndex - 1]"
:deploy-id="props.deployId"
:data-id="props.dataId"
:assignee="props.assignee"
:user-type="props.userType"
@update:approval-data="handleApprovalDataUpdate"
@success="handleApprovalSuccess"
@approval-fail="handleApprovalFail" @approval-fail="handleApprovalFail"
/> />
</a-col> --> </a-col>
</a-row> </a-row>
</div> </div>
<template #extra> <template #extra>
...@@ -76,16 +59,11 @@ import { ref, computed, onMounted, watch, nextTick } from 'vue' ...@@ -76,16 +59,11 @@ import { ref, computed, onMounted, watch, nextTick } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import HistoryPanel from './HistoryPanel.vue' import HistoryPanel from './HistoryPanel.vue'
import CurrentFormPanel from './CurrentFormPanel.vue' import CurrentFormPanel from './CurrentFormPanel.vue'
import ApprovalPanel from './ApprovalPanel.vue'
import FlowHistoryDrawer from './FlowHistoryDrawer.vue'; import FlowHistoryDrawer from './FlowHistoryDrawer.vue';
import { useDrawer } from '/@/components/Drawer'; import { useDrawer } from '/@/components/Drawer';
const formBpm = ref(true) const formBpm = ref(true)
const formDisabled = ref(false)
const showHistoryVisible = ref(true) const showHistoryVisible = ref(true)
const [refFlowHistoryDrawer, { openDrawer }] = useDrawer(); const [refFlowHistoryDrawer, { openDrawer }] = useDrawer();
interface WorkflowNode { interface WorkflowNode {
...@@ -105,137 +83,72 @@ interface WorkflowNode { ...@@ -105,137 +83,72 @@ interface WorkflowNode {
} }
const props = defineProps({ const props = defineProps({
title: { title: { type: String, default: '工作流表单' },
type: String, visible: { type: Boolean, default: false },
default: '工作流表单' maskClosable: { type: Boolean, default: false },
}, currentNodeIndex: { type: Number, required: true, default: 2 },
visible: { workflowNodes: { type: Array as () => WorkflowNode[], required: true, default: () => [] },
type: Boolean, externalFormData: { type: Object as () => Record<string, any>, default: () => ({}) },
default: false formData: { type: Object as () => Record<string, any>, default: () => ({}) },
}, deployId: { type: String, default: '' },
maskClosable: { taskId: { type: String, default: '' },
type: Boolean, procInsId: { type: String, default: '' },
default: false dataId: { type: String, default: '' },
}, showHistoryFormData: { type: Boolean, default: false },
showApprovalPanel: { type: Boolean, default: false },
currentNodeIndex: {
type: Number,
required: true,
default: 2
},
workflowNodes: {
type: Array as () => WorkflowNode[],
required: true,
default: () => []
},
externalFormData: {
type: Object as () => Record<string, any>,
default: () => ({})
},
formData: {
type: Object as () => Record<string, any>,
default: () => ({})
},
deployId: {
type: String,
default: ''
},
taskId: {
type: String,
default: ''
},
procInsId: {
type: String,
default: ''
},
dataId: {
type: String,
default: ''
},
showHistoryFormData: {
type: Boolean,
default: false
},
showApprovalPanel: {
type: Boolean,
default: false
},
flowNodes: {
type: Array as () => WorkflowNode[],
default: () => []
},
currentFlowNodeIdx: {
type: Number,
default: 0
},
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%'}
}) })
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'])
// 状态
const submitLoading = ref(false) const submitLoading = ref(false)
const currentFormData = ref<any>({}) const currentFormData = ref<any>({})
const approvalData = ref<any>({}) const approvalData = ref<any>({})
const currentFormPanelRef = ref<InstanceType<typeof CurrentFormPanel> | null>(null) const currentFormPanelRef = ref<any>(null)
const approvalPanelRef = ref<InstanceType<typeof ApprovalPanel> | null>(null)
// 计算属性
const drawerTitle = computed(() => props.title) const drawerTitle = computed(() => props.title)
const showApprovalPanel = computed(() => props.showApprovalPanel)
// 只读节点:索引小于 currentNodeIndex 的节点
const readonlyNodes = computed(() => { const readonlyNodes = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) { if (!props.workflowNodes?.length) return []
return []
}
const idx = props.currentNodeIndex const idx = props.currentNodeIndex
if (idx <= 0) return [] if (idx <= 0) return []
return props.workflowNodes.slice(0, idx) return props.workflowNodes.slice(0, idx)
}) })
// 可编辑节点:索引等于 currentNodeIndex 的节点
const editableNode = computed(() => { const editableNode = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) { if (!props.workflowNodes?.length) return null
return null
}
const idx = props.currentNodeIndex const idx = props.currentNodeIndex
if (idx < 0 || idx >= props.workflowNodes.length) { if (idx < 0 || idx >= props.workflowNodes.length) return null
return null
}
return props.workflowNodes[idx] return props.workflowNodes[idx]
}) })
// 处理表单数据更新
function handleFormDataUpdate(data: any) { function handleFormDataUpdate(data: any) {
currentFormData.value = data currentFormData.value = data
emit('form-data-update', data) emit('form-data-update', data)
} handleClose()
// 处理审核数据更新
function handleApprovalDataUpdate(data: any) {
approvalData.value = data
} }
function handleFormMounted({ nodeId, instance }: { nodeId: string; instance: any }) { function handleFormMounted({ nodeId, instance }: { nodeId: string; instance: any }) {
console.log('表单组件已挂载 - 节点:', nodeId) console.log('表单组件已挂载 - 节点:', nodeId)
} }
// 处理审核成功
function handleApprovalSuccess(dataId: string) { function handleApprovalSuccess(dataId: string) {
if (dataId === props.dataId) { if (dataId === props.dataId) {
message.success('审核成功') message.success('审核成功')
emit('success',dataId) emit('success', dataId)
handleClose()
}
}
function handleApprovalFail(dataId: string) {
if (dataId === props.dataId) {
emit('approval-fail', dataId)
handleClose() handleClose()
} }
} }
// 关闭抽屉
function handleClose() { function handleClose() {
emit('update:visible', false) emit('update:visible', false)
emit('close') emit('close')
...@@ -244,35 +157,25 @@ function handleClose() { ...@@ -244,35 +157,25 @@ function handleClose() {
function handleHistoryHidden() { function handleHistoryHidden() {
showHistoryVisible.value = !showHistoryVisible.value showHistoryVisible.value = !showHistoryVisible.value
} }
// 处理审核失败
function handleApprovalFail(dataId: string) {
if (dataId === props.dataId) {
emit('approval-fail',dataId)
handleClose()
}
}
// 重置数据
function resetFormData() { function resetFormData() {
if (currentFormPanelRef.value) { if (currentFormPanelRef.value) {
currentFormPanelRef.value.resetFormData() currentFormPanelRef.value.resetFormData()
} }
if (approvalPanelRef.value&&props.showApprovalPanel) {
(approvalPanelRef.value as any).resetForm()
}
currentFormData.value = {} currentFormData.value = {}
approvalData.value = {} approvalData.value = {}
} }
const openHistoryDrawer = () => { const openHistoryDrawer = () => {
openDrawer(true,{ openDrawer(true, {
procInsId: props.procInsId, procInsId: props.procInsId,
dataId: props.dataId, dataId: props.dataId,
deployId: props.deployId, deployId: props.deployId,
} ); });
}; };
// 监听抽屉打开
watch(() => props.visible, async (newVal) => { watch(() => props.visible, async (newVal) => {
if (newVal) { if (newVal) {
resetFormData() resetFormData()
...@@ -283,7 +186,6 @@ watch(() => props.visible, async (newVal) => { ...@@ -283,7 +186,6 @@ watch(() => props.visible, async (newVal) => {
} }
}) })
// 监听 dataId 变化
watch(() => props.dataId, async () => { watch(() => props.dataId, async () => {
if (props.visible && currentFormPanelRef.value) { if (props.visible && currentFormPanelRef.value) {
await nextTick() await nextTick()
...@@ -291,16 +193,13 @@ watch(() => props.dataId, async () => { ...@@ -291,16 +193,13 @@ watch(() => props.dataId, async () => {
} }
}) })
// 监听外部数据变化
watch(() => props.externalFormData, (newData) => { watch(() => props.externalFormData, (newData) => {
if (newData && editableNode.value && newData[editableNode.value.id]) { if (newData && editableNode.value && newData[editableNode.value.id]) {
currentFormData.value = newData[editableNode.value.id] currentFormData.value = newData[editableNode.value.id]
} }
}, { deep: true }) }, { deep: true })
onMounted(() => { onMounted(() => { resetFormData() })
resetFormData()
})
defineExpose({ defineExpose({
resetFormData, resetFormData,
...@@ -320,70 +219,20 @@ defineExpose({ ...@@ -320,70 +219,20 @@ defineExpose({
:deep(.ant-drawer-header) { :deep(.ant-drawer-header) {
background-color: #f5f7fa; background-color: #f5f7fa;
border-bottom: 1px solid #e8eef2; border-bottom: 1px solid #e8eef2;
padding: 10px 10px; padding: 10px;
.ant-drawer-title { .ant-drawer-title {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
color: #1f2f3d; color: #1f2f3d;
} }
.ant-drawer-close { color: #8c8c8c; &:hover { color: #1f2f3d; } }
.ant-drawer-close {
color: #8c8c8c;
&:hover {
color: #1f2f3d;
}
} }
} :deep(.ant-drawer-body) { padding: 5px; height: 100%; overflow: hidden; }
:deep(.ant-drawer-footer) { padding: 5px 10px; border-top: 1px solid #e8eef2; background-color: #fff; }
:deep(.ant-drawer-body) {
padding: 5px;
height: 100%;
overflow: hidden;
}
:deep(.ant-drawer-footer) {
padding: 5px 10px;
border-top: 1px solid #e8eef2;
background-color: #fff;
}
}
.drawer-container {
height: 100%;
overflow: hidden;
background: #eef5f680;
padding: 1px;
margin: 0;
}
.drawer-row {
height: 100%;
flex-wrap: nowrap;
background: #0c37a418;
}
.drawer-col {
height: 100%;
&:not(:last-child) {
margin-right: 16px;
}
}
.panel-card {
height: 100%;
:deep(.ant-card-body) {
height: 100%;
display: flex;
flex-direction: column;
}
}
.drawer-footer {
display: flex;
justify-content: flex-end;
gap: 12px;
} }
.drawer-container { height: 100%; overflow: hidden; background: #eef5f680; padding: 1px; margin: 0; }
.drawer-row { height: 100%; flex-wrap: nowrap; background: #0c37a418; }
.drawer-col { height: 100%; &:not(:last-child) { margin-right: 16px; } }
.panel-card { height: 100%; :deep(.ant-card-body) { height: 100%; display: flex; flex-direction: column; } }
.drawer-footer { display: flex; justify-content: flex-end; gap: 12px; }
</style> </style>
...@@ -8,33 +8,38 @@ ...@@ -8,33 +8,38 @@
:minHeight="400" :minHeight="400"
:useWrapper="true" :useWrapper="true"
:wrapperFooterOffset="20" :wrapperFooterOffset="20"
@cancel=""
@ok="handleOk" @ok="handleOk"
> >
<ApprovalPanel ref="approvalPanelRef" /> <ApprovalPanel ref="approvalPanelRef" @success="handleSuccess" @approval-fail="handleApprovalFail"/>
</BasicModal> </BasicModal>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue' import { ref } from 'vue'
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue'; import ApprovalPanel from './ApprovalPanel.vue';
import { complete, getMyTaskFlow,rejectTask, assignRead,assign } from '/@/components/Process/api/todo';
const approvalPanelRef = ref(); const approvalPanelRef = ref();
const emit = defineEmits(['success','approval-fail','update-form-data']) 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 });
approvalPanelRef.value?.initFormData?.(data);
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
}); });
function handleOk() { function handleOk() {
alert('handleOk'); setModalProps({ confirmLoading: true });
setModalProps({confirmLoading: true}); approvalPanelRef.value?.handleSaveApp?.();
approvalPanelRef.value.handleSaveApp(); setModalProps({ confirmLoading: false });
setModalProps({confirmLoading: false});
} }
function handleSuccess(data: any) {
emit('success', data)
closeModal()
}
function handleApprovalFail(data: any) {
emit('approval-fail', data)
closeModal()
}
</script> </script>
...@@ -30,23 +30,19 @@ ...@@ -30,23 +30,19 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, onMounted, nextTick } from 'vue'; import { ref, 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, 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({
beforeFlowNode: { const beforeFlowNode = ref<any>({})
type: Object, const assignee = ref()
default: () => ({}), const userType = ref('')
}, const deployId = ref('')
assignee: { type: String, default: null }, const dataId = ref('')
userType: { type: String, default: '' },
deployId: { type: String, default: '' },
dataId: { type: String, default: '' },
});
const formSchemas: FormSchema[] = [ const formSchemas: FormSchema[] = [
{ {
...@@ -60,7 +56,7 @@ ...@@ -60,7 +56,7 @@
label: '用户类型', label: '用户类型',
field: 'userType', field: 'userType',
component: 'Input', component: 'Input',
defaultValue: props.userType, defaultValue: '',
ifShow: false, ifShow: false,
}, },
{ {
...@@ -112,16 +108,17 @@ ...@@ -112,16 +108,17 @@
labelWidth: '120px', labelWidth: '120px',
size: 'large', size: 'large',
baseColProps: {style: { width: '100%' } }, 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: deployId.value, dataId: dataId.value });
if (myTaskFlow?.taskId) { if (myTaskFlow?.taskId) {
const reviewStatus = formDataTmp.reviewStatus; const reviewStatus = formDataTmp.reviewStatus;
if (reviewStatus === true) { if (reviewStatus === true) {
...@@ -132,9 +129,13 @@ ...@@ -132,9 +129,13 @@
comment: formDataTmp.comment, comment: formDataTmp.comment,
values: { values: {
approval: formDataTmp.approvalUser || formDataTmp.approvalRole, approval: formDataTmp.approvalUser || formDataTmp.approvalRole,
approvalType: props.userType, approvalType: userType.value,
}, },
}); });
emit('success', {
dataId: dataId.value,
bpmNodeId: myTaskFlow.taskDefinitionKey || '',
});
} else { } else {
await rejectTask({ await rejectTask({
instanceId: myTaskFlow.procInsId || '', instanceId: myTaskFlow.procInsId || '',
...@@ -142,18 +143,18 @@ ...@@ -142,18 +143,18 @@
taskId: myTaskFlow.taskId, taskId: myTaskFlow.taskId,
comment: formDataTmp.comment, comment: formDataTmp.comment,
values: { values: {
rejectNode: props.beforeFlowNode, rejectNode: beforeFlowNode.value,
}, },
}); });
emit('approval-fail', {
emit('approval-fail', props.dataId); dataId: dataId.value,
bpmNodeId: beforeFlowNode.value.id
});
} }
} }
emit('success', props.dataId);
nextTick(() => { nextTick(() => {
message.success('任务发送成功'); //message.success('任务发送成功');
handleClose(); //handleClose();
}); });
}; };
...@@ -173,7 +174,7 @@ ...@@ -173,7 +174,7 @@
}; };
const handleClickPass = async () => { const handleClickPass = async () => {
updateField(props.userType); updateField(userType.value);
}; };
const updateField = (userTp) => { const updateField = (userTp) => {
...@@ -182,6 +183,7 @@ ...@@ -182,6 +183,7 @@
{ {
field: 'approvalUser', field: 'approvalUser',
required: true, required: true,
defaultValue: assignee.value,
ifShow: true, ifShow: true,
}, },
{ {
...@@ -190,11 +192,18 @@ ...@@ -190,11 +192,18 @@
ifShow: false, ifShow: false,
}, },
]); ]);
nextTick(async () => {
await setFieldsValue({
userType: userType.value,
approvalUser: assignee.value,
})
})
} else { } else {
updateSchema([ updateSchema([
{ {
field: 'approvalRole', field: 'approvalRole',
required: true, required: true,
defaultValue: assignee.value,
ifShow: true, ifShow: true,
}, },
{ {
...@@ -203,15 +212,30 @@ ...@@ -203,15 +212,30 @@
ifShow: false, ifShow: false,
}, },
]); ]);
nextTick(async () => {
await setFieldsValue({
userType: userType.value,
approvalRole: assignee.value,
})
})
} }
}; };
onMounted(async () => { const initFormData = async (data) => {
updateField(props.userType); beforeFlowNode.value = data.beforeFlowNode || {}
}); assignee.value = data.assignee || ''
userType.value = data.userType || ''
deployId.value = data.deployId || ''
dataId.value = data.dataId || ''
nextTick(async () => {
updateField(userType.value);
})
}
defineExpose({ defineExpose({
handleSaveApp, handleSaveApp,
initFormData,
}) })
</script> </script>
......
...@@ -2,245 +2,80 @@ ...@@ -2,245 +2,80 @@
<a-form layout="vertical"> <a-form layout="vertical">
<a-form-item label="用户类型" required> <a-form-item label="用户类型" required>
<a-radio-group v-model:value="localUserType" disabled> <a-radio-group v-model:value="localUserType" disabled>
<a-radio value="user"> <a-radio value="user"><UserOutlined /> 用户</a-radio>
<UserOutlined /> 用户 <a-radio value="role"><TeamOutlined /> 角色</a-radio>
</a-radio>
<a-radio value="role">
<TeamOutlined /> 角色
</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<!-- 用户/角色选择区域(未选择时) --> <a-form-item v-if="!assigneeId" :label="localUserType === 'user' ? '选择用户' : '选择角色'">
<a-form-item
v-if="!assigneeId"
:label="localUserType === 'user' ? '选择用户' : '选择角色'"
>
<div class="assignee-selector"> <div class="assignee-selector">
<a-input <a-input :value="assigneeDisplayName" :placeholder="`点击'选择'按钮选择${localUserType === 'user' ? '用户' : '角色'}`" readonly class="assignee-input">
:value="assigneeDisplayName"
:placeholder="`点击'选择'按钮选择${localUserType === 'user' ? '用户' : '角色'}`"
readonly
class="assignee-input"
>
<template #suffix> <template #suffix>
<a-button type="link" size="small" @click="handleSelect">选择</a-button> <a-button type="link" size="small" @click="handleSelect">选择</a-button>
</template> </template>
</a-input> </a-input>
</div> </div>
</a-form-item> </a-form-item>
<!-- 已指定信息展示 -->
<div v-if="assigneeId" 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="2" size="small" bordered> <a-descriptions :column="2" size="small" bordered>
<a-descriptions-item :label="localUserType === 'user' ? '用户ID' : '角色ID'"> <a-descriptions-item :label="localUserType === 'user' ? '用户ID' : '角色ID'">{{ assigneeId }}</a-descriptions-item>
{{ assigneeId }} <a-descriptions-item :label="localUserType === 'user' ? '用户名' : '角色名'">{{ assigneeDisplayName || '--' }}</a-descriptions-item>
</a-descriptions-item> <a-descriptions-item><a-button type="link" size="small" @click="clearAssignee" class="change-btn">重新选择</a-button></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> </a-descriptions>
</div> </div>
<a-form-item label="说明"> <a-form-item label="说明">
<a-textarea <a-textarea v-model:value="assigneeRemark" placeholder="请输入说明" :rows="4" allow-clear show-count :maxlength="200" />
v-model:value="assigneeRemark"
placeholder="请输入说明"
:rows="4"
allow-clear
show-count
:maxlength="200"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
<RoleSelectModal <RoleSelectModal rowKey="id" @register="registerSelRoleModal" @getSelectResult="onSelectRoleOk" isRadioSelection :showButton="false" labelKey="roleName" />
rowKey="id" <UserSelectModal rowKey="id" @register="registerSelUserModal" @getSelectResult="onSelectUserOk" isRadioSelection :showButton="false" labelKey="realname" />
@register="registerSelRoleModal"
@getSelectResult="onSelectRoleOk"
isRadioSelection
:showButton="false"
labelKey="roleName"
/>
<UserSelectModal
rowKey="id"
@register="registerSelUserModal"
@getSelectResult="onSelectUserOk"
isRadioSelection
:showButton="false"
labelKey="realname"
/>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, watch, nextTick } from 'vue' import { ref } from 'vue'
import { useModal } from '/@/components/Modal' import { useModal } from '/@/components/Modal'
import { UserOutlined, TeamOutlined } from '@ant-design/icons-vue' import { UserOutlined, TeamOutlined } from '@ant-design/icons-vue'
import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue' import UserSelectModal from '/@/components/Form/src/jeecg/components/modal/UserSelectModal.vue'
import RoleSelectModal from '/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue' import RoleSelectModal from '/@/components/Form/src/jeecg/components/modal/RoleSelectModal.vue'
const emit = defineEmits(['success', 'error', 'close'])
const [registerSelUserModal, { openModal: userOpenModal }] = useModal() const [registerSelUserModal, { openModal: userOpenModal }] = useModal()
const [registerSelRoleModal, { openModal: roleOpenModal }] = useModal() const [registerSelRoleModal, { openModal: roleOpenModal }] = useModal()
// 状态
const localUserType = ref<'user' | 'role'>('user') const localUserType = ref<'user' | 'role'>('user')
const assigneeId = ref('') const assigneeId = ref('')
const assigneeDisplayName = ref('') const assigneeDisplayName = ref('')
const assigneeRemark = ref('') const assigneeRemark = ref('')
const setAssigneeData = ((data) => { const setAssigneeData = (data: any) => {
if (data) { if (data) {
if (data.assignee) {
localUserType.value = data.userType === 'role' ? 'role' : 'user' localUserType.value = data.userType === 'role' ? 'role' : 'user'
if (data.assignee) {
assigneeId.value = data.assignee assigneeId.value = data.assignee
if (data.assigneeName) { assigneeDisplayName.value = 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 clearAssignee = () => { assigneeId.value = ''; assigneeDisplayName.value = '' }
const handleSelect = () => { localUserType.value === 'user' ? userOpenModal() : roleOpenModal() }
const onSelectUserOk = (options: any[], values: any[]) => { const onSelectUserOk = (options: any[], values: any[]) => {
if (!values?.length) return if (!values?.length) return
assigneeId.value = values[0] assigneeId.value = values[0]
assigneeDisplayName.value = options[0]?.label || '' assigneeDisplayName.value = options[0]?.label || ''
localUserType.value = 'user' localUserType.value = 'user'
} }
const onSelectRoleOk = (options: any[], values: any[]) => { const onSelectRoleOk = (options: any[], values: any[]) => {
if (!values?.length) return if (!values?.length) return
assigneeId.value = values[0] assigneeId.value = values[0]
assigneeDisplayName.value = options[0]?.label || '' assigneeDisplayName.value = options[0]?.label || ''
localUserType.value = 'role' localUserType.value = 'role'
} }
const getAssigneeData = () => ({ userType: localUserType.value, assignee: assigneeId.value, name: assigneeDisplayName.value, remark: assigneeRemark.value })
// 对外暴露的方法 defineExpose({ setAssigneeData, getAssigneeData })
const getAssigneeData = () => ({
userType: localUserType.value,
assignee: assigneeId.value,
name: assigneeDisplayName.value,
remark: assigneeRemark.value
})
defineExpose({
setAssigneeData,
getAssigneeData,
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.drawer-content { .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; } } } }
height: 100%; .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; } } }
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> </style>
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import ApprovalPanel from './ApprovalPanel.vue'; import ApprovalPanel from './ApprovalPanel.vue';
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter}); setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
}); });
......
...@@ -21,35 +21,29 @@ ...@@ -21,35 +21,29 @@
import { ref } from 'vue' import { ref } from 'vue'
import { BasicModal, useModalInner } from '/@/components/Modal'; import { BasicModal, useModalInner } from '/@/components/Modal';
import AssigneePanel from './AssigneePanel.vue'; import AssigneePanel from './AssigneePanel.vue';
import { complete, getMyTaskFlow,rejectTask, assignRead,assign } from '/@/components/Process/api/todo'; 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 emit = defineEmits(['success', 'approval-fail', 'update-form-data'])
const taskTitle = ref('任务') const taskTitle = ref('任务')
const sending = ref(false); const sending = ref(false);
const taskType = ref<any>({}); const taskType = ref<any>(null);
const refAssigneePanel = ref<any>(null); const refAssigneePanel = ref<any>(null);
const myTaskFlow = ref<any>({}); const myTaskFlow = ref<any>({});
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => { const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter}); setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
taskTitle.value = data?.taskTitle || '任务' taskTitle.value = data?.taskTitle || '任务'
taskType.value = data?.taskType || ""; taskType.value = data?.taskType || ''
refAssigneePanel.value?.setAssigneeData(data)
refAssigneePanel.value.setAssigneeData(data); myTaskFlow.value = await getMyTaskFlow({ deploymentId: data?.deployId || '', dataId: data?.dataId || '' })
myTaskFlow.value = await getMyTaskFlow({
deploymentId: data?.deployId || '',
dataId: data?.dataId || '',
})
}); });
const handleTaskOk = async () => { const handleTaskOk = async () => {
if (sending.value) return; if (sending.value) return
try { try {
sending.value = true; sending.value = true
const assigneeData = refAssigneePanel.value.getAssigneeData(); const assigneeData = refAssigneePanel.value?.getAssigneeData() || {}
const submitData: any = { const submitData: any = {
instanceId: myTaskFlow.value.procInsId, instanceId: myTaskFlow.value.procInsId,
deployId: myTaskFlow.value.deployId, deployId: myTaskFlow.value.deployId,
...@@ -57,55 +51,38 @@ ...@@ -57,55 +51,38 @@
comment: assigneeData.remark || '', comment: assigneeData.remark || '',
values: { values: {
approval: assigneeData.assignee, approval: assigneeData.assignee,
approvalType: assigneeData.userType, 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('操作成功'); if (taskType.value === 'send') await handleSendTask(submitData)
emit('success', submitData); else if (taskType.value === 'reject') await handleRejectTask(submitData)
closeThisModal(); else if (taskType.value === 'transmit') await handleTransmitTask(submitData)
} catch (error: any) { else if (taskType.value === 'read') await handleReadTask(submitData)
emit('success', submitData)
closeThisModal()
} catch (error) {
console.error('任务操作失败:', error)
} finally { } finally {
sending.value = false; 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();
}
const handleSendTask = async (submitData: any) => {
await complete(submitData)
}
const handleRejectTask = async (submitData: any) => {
await rejectTask(submitData)
}
const handleReadTask = async (submitData: any) => {
alert(JSON.stringify(submitData))
await assignRead(submitData)
}
const handleTransmitTask = async (submitData: any) => {
await assign(submitData)
}
const closeThisModal = () => { closeModal() }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.assignee-panel-wrapper{ .assignee-panel-wrapper { padding: 0px; }
padding: 0px;
}
</style> </style>
...@@ -220,7 +220,7 @@ export const formSchema: FormSchema[] = [ ...@@ -220,7 +220,7 @@ export const formSchema: FormSchema[] = [
}, },
{ {
label: '流程节点顺序号', label: '流程节点顺序号',
field: 'bmpNodeId', field: 'bpmNodeId',
component: 'Input', component: 'Input',
}, },
{ {
...@@ -327,7 +327,7 @@ export const superQuerySchema = { ...@@ -327,7 +327,7 @@ export const superQuerySchema = {
execEndDate: {title: '整改结束时间',order: 22,view: 'date', type: 'string',}, execEndDate: {title: '整改结束时间',order: 22,view: 'date', type: 'string',},
execRemark: {title: '整改落实情况',order: 23,view: 'textarea', type: 'string',}, execRemark: {title: '整改落实情况',order: 23,view: 'textarea', type: 'string',},
reviewComment: {title: '审核意见',order: 24,view: 'textarea', type: 'string',}, reviewComment: {title: '审核意见',order: 24,view: 'textarea', type: 'string',},
bmpNodeId: {title: '流程节点顺序号',order: 25,view: 'text', type: 'string',}, bpmNodeId: {title: '流程节点顺序号',order: 25,view: 'text', type: 'string',},
bpmStatus: {title: '流程状态',order: 26,view: 'text', type: 'string',}, bpmStatus: {title: '流程状态',order: 26,view: 'text', type: 'string',},
deployId: {title: '部署ID',order: 27,view: 'text', type: 'string',}, deployId: {title: '部署ID',order: 27,view: 'text', type: 'string',},
createdUser: {title: '创建人',order: 28,view: 'text', type: 'string',}, createdUser: {title: '创建人',order: 28,view: 'text', type: 'string',},
......
<template> <template>
<div> <div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题--> <template #tableTitle></template>
<template #tableTitle>
</template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)"/> <TableAction :actions="getTableAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal @register="registerExecuteModal" @success="handleSuccess" />
</div> </div>
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns } from './StProblemCheck.data';
import { list,problemArchive} from './StProblemCheck.api'; import { list, problemArchive } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','openMultiForm'])
const props = defineProps({ const props = defineProps({
currentFlowNode: { currentFlowNode: { type: Object, default: () => ({}) },
type: Object, });
default: () => ({})
},
})
const emit = defineEmits(['callback', 'openMultiForm']);
//注册model const { tableContext } = useListPage({
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
//注册table数据
const { prefixCls, tableContext } = useListPage({
tableProps: { tableProps: {
title: '问题归档', title: '问题归档',
api: list, api: list,
columns, columns,
canResize: false, canResize: false,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
},
actionColumn: {
width: 200,
fixed: 'right',
}, },
actionColumn: { width: 200, fixed: 'right' },
}, },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handleArchive(record: Recordable) { function handleArchive(record: Recordable) {
problemArchive({id: record.id, }).then(handleSuccess); problemArchive({ id: record.id }).then(handleSuccess);
} }
function handleSuccess() { function handleSuccess() {
(selectedRowKeys.value = []) && reload(); selectedRowKeys.value = [];
reload();
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [{ label: '问题归档', onClick: handleArchive.bind(null, record) }];
{
label: '问题归档',
onClick: handleArchive.bind(null, record),
},
];
} }
</script> </script>
<style scoped></style> <style scoped></style>
import {defHttp} from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage"; import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage(); const { createConfirm } = useMessage();
enum Api { const enum Api {
list = '/problem/stProblemCheck/list', list = '/problem/stProblemCheck/list',
save='/problem/stProblemCheck/add', save='/problem/stProblemCheck/add',
edit='/problem/stProblemCheck/edit', edit='/problem/stProblemCheck/edit',
...@@ -12,35 +12,22 @@ enum Api { ...@@ -12,35 +12,22 @@ enum Api {
importExcel = '/problem/stProblemCheck/importExcel', importExcel = '/problem/stProblemCheck/importExcel',
exportXls = '/problem/stProblemCheck/exportXls', exportXls = '/problem/stProblemCheck/exportXls',
problemArchive = '/problem/stProblemCheck/problemArchive', problemArchive = '/problem/stProblemCheck/problemArchive',
queryById = '/problem/stProblemCheck/queryById',
} }
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls; export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel; export const getImportUrl = Api.importExcel;
/**
* 列表接口
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/** export const list = (params) => defHttp.get({url: Api.list, params});
* 删除单个
*/ export const queryById = (params) => defHttp.get({url: Api.queryById, params});
export const deleteOne = (params,handleSuccess) => {
export const deleteOne = (params, handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess(); handleSuccess?.();
}); });
} }
/**
* 批量删除
* @param params
*/
export const batchDelete = (params, handleSuccess) => { export const batchDelete = (params, handleSuccess) => {
createConfirm({ createConfirm({
iconType: 'warning', iconType: 'warning',
...@@ -50,26 +37,16 @@ export const batchDelete = (params, handleSuccess) => { ...@@ -50,26 +37,16 @@ export const batchDelete = (params, handleSuccess) => {
cancelText: '取消', cancelText: '取消',
onOk: () => { onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess(); handleSuccess?.();
}); });
} }
}); });
} }
/**
* 保存或者更新 export const saveOrUpdate = (params, isUpdate = false) => {
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save; let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params}); return defHttp.post({url: url, params});
} }
/** export const problemArchive = (params) => defHttp.post({url: Api.problemArchive, params});
* 保存或者更新
* @param params
*/
export const problemArchive = (params) => {
let url = Api.problemArchive;
return defHttp.post({url: url, params});
}
import { BasicColumn } from '/@/components/Table'; import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table'; import { FormSchema } from '/@/components/Table';
import { render } from '/@/utils/common/renderUtils'; import { render } from '/@/utils/common/renderUtils';
//列表数据
export const columns: BasicColumn[] = [ export const columns: BasicColumn[] = [
{ {
title: '问题编号', title: '问题编号',
...@@ -357,29 +357,14 @@ export const reviewFormSchema: FormSchema[] = [ ...@@ -357,29 +357,14 @@ export const reviewFormSchema: FormSchema[] = [
}, },
]; ];
/**
* 流程表单调用这个方法获取formSchema-默认弹窗(查看详情)
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] { export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema; return formSchema;
} }
/**
* 流程表单调用这个方法获取formSchema-整改计划
* @param param
*/
export function getPlanFormSchema(_planFormSchema): FormSchema[] { export function getPlanFormSchema(_planFormSchema): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return planFormSchema; return planFormSchema;
} }
/**
* 流程表单调用这个方法获取formSchema-整改执行
* @param param
*/
export function getExecuteFormSchema(_executeFormSchema): FormSchema[] { export function getExecuteFormSchema(_executeFormSchema): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return executeFormSchema; return executeFormSchema;
} }
...@@ -18,50 +18,35 @@ ...@@ -18,50 +18,35 @@
</a-button> </a-button>
</a-dropdown> </a-dropdown>
</template> </template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" /> <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 表单区域 -->
<StProblemCheckModal @register="registerModal" @success="handleSuccess" :center="true" /> <StProblemCheckModal @register="registerModal" @success="handleSuccess" :center="true" />
</div> </div>
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { ref } from 'vue'; import { ref, unref } 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 { Icon } from '/@/components/Icon';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import StProblemCheckModal from './components/StProblemCheckModal.vue'; import StProblemCheckModal from './components/StProblemCheckModal.vue';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns, searchFormSchema } from './StProblemCheck.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl,saveOrUpdate } from './StProblemCheck.api'; import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, saveOrUpdate } from './StProblemCheck.api';
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: { type: Object, default: () => ({}) },
type: Object, currentFlowNode: { type: Object, default: () => ({}) },
default: () => ({}) nextFlowNode: { type: Object, default: () => ({}) },
}, todoList: { type: Array, default: () => [] },
currentFlowNode: { });
type: Object,
default: () => ({})
},
nextFlowNode: {
type: Object,
default: () => ({})
},
todoList: {
type: Array,
default: () => []
}
})
const emit = defineEmits(['callback','startWorkFlow','sendWorkFlow','openMultiForm']) const emit = defineEmits(['callback', 'startWorkFlow', 'sendWorkFlow', 'openMultiForm']);
//注册model
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerPlanModal, { openModal: openPlanModal }] = useModal(); const [registerPlanModal, { openModal: openPlanModal }] = useModal();
//注册table数据
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({ const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
tableProps: { tableProps: {
title: '问题整改', title: '问题整改',
...@@ -69,55 +54,31 @@ ...@@ -69,55 +54,31 @@
columns, columns,
canResize: false, canResize: false,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
params['todoList'] = props.todoList params['todoList'] = props.todoList;
},
actionColumn: {
width: 200,
fixed: 'right',
},
},
exportConfig: {
name: '问题整改',
url: getExportUrl,
}, },
importConfig: { actionColumn: { width: 200, fixed: 'right' },
url: getImportUrl,
success: handleSuccess,
}, },
exportConfig: { name: '问题整改', url: getExportUrl },
importConfig: { url: getImportUrl, success: handleSuccess },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
/**
* 新增事件
*/
function handleAdd() { function handleAdd() {
openModal(true, { openModal(true, {
isUpdate: false, isUpdate: false,
showFooter: true, showFooter: true,
record: { record: { bpmNodeId: props.currentFlowNode.id, deployId: props.currentFlowNode.deployId, bpmStatus: 0 },
bmpNodeId: props.currentFlowNode.id,
deployId: props.currentFlowNode.deployId,
bpmStatus: 0,
}
}); });
} }
function handleEdit(record: Recordable) { function handleEdit(record: Recordable) {
openModal(true, { openModal(true, { record, isUpdate: true, showFooter: true });
record,
isUpdate: true,
showFooter: true,
});
} }
function handleDetail(record: Recordable) { function handleDetail(record: Recordable) {
openModal(true, { openModal(true, { record, isUpdate: true, showFooter: false });
record,
isUpdate: true,
showFooter: false,
});
} }
async function handleDelete(record) { async function handleDelete(record) {
...@@ -125,77 +86,50 @@ ...@@ -125,77 +86,50 @@
} }
async function batchHandleDelete() { async function batchHandleDelete() {
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess); await batchDelete({ ids: unref(selectedRowKeys) }, handleSuccess);
} }
function handleSuccess(retData) { function handleSuccess(retData) {
(selectedRowKeys.value = []) && reload(); selectedRowKeys.value = [];
if(retData){ reload();
emit('startWorkFlow',retData) if (retData) emit('startWorkFlow', retData);
}
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [
{ { label: '编辑', onClick: handleEdit.bind(null, record) },
label: '编辑', { label: '发送任务', onClick: handleFlow.bind(null, record) },
onClick: handleEdit.bind(null, record),
},
{
label: '发送任务',
onClick: handleFlow.bind(null, record),
},
]; ];
} }
function getDropDownAction(record) { function getDropDownAction(record) {
return [ return [
{ { label: '详情', onClick: handleDetail.bind(null, record) },
label: '详情',
onClick: handleDetail.bind(null, record),
},
{ {
label: '删除', label: '删除',
popConfirm: { popConfirm: { title: '是否确认删除', confirm: handleDelete.bind(null, record) },
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
},
}, },
]; ];
} }
async function handleFlow(record: Recordable) { async function handleFlow(record: Recordable) {
emit("sendWorkFlow",record) emit('sendWorkFlow', record);
} }
async function handleUpdate(dataId,flowNode) { async function handleUpdate(dataId, flowNode) {
let record = { const record = { bpmNodeId: flowNode.id, deployId: flowNode.deployId, bpmStatus: 2, id: dataId };
bmpNodeId: flowNode.id, await saveOrUpdate(record, true);
deployId: flowNode.deployId, selectedRowKeys.value = [];
bpmStatus: 2, reload();
id:dataId
}
await saveOrUpdate(record,true).then(res => {
(selectedRowKeys.value = []) && reload();
})
} }
async function handleStartUpdate(flowData) { async function handleStartUpdate(flowData) {
let record = { const record = { procInsId: flowData.procInsId, id: flowData.dataId, bpmStatus: 0 };
procInsId: flowData.procInsId, await saveOrUpdate(record, true);
id:flowData.dataId,
bpmStatus: 0,
}
await saveOrUpdate(record,true).then(res => {
handleSuccess(null); handleSuccess(null);
})
} }
defineExpose({ defineExpose({ handleUpdate, handleStartUpdate });
handleUpdate,
handleStartUpdate
})
</script> </script>
<style scoped></style> <style scoped></style>
<template> <template>
<div> <div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题--> <template #tableTitle></template>
<template #tableTitle>
</template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)"/> <TableAction :actions="getTableAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal @register="registerExecuteModal" @success="handleSuccess" />
</div> </div>
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { BasicTable, TableAction } from '/@/components/Table'; import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns } from './StProblemCheck.data';
import { list, saveOrUpdate } from './StProblemCheck.api'; import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: { type: Object, default: () => ({}) },
type: Object, currentFlowNode: { type: Object, default: () => ({}) },
default: () => ({}) nextFlowNode: { type: Object, default: () => ({}) },
}, });
currentFlowNode: {
type: Object, const emit = defineEmits(['callback', 'sendWorkFlow', 'openMultiForm']);
default: () => ({})
},
nextFlowNode: {
type: Object,
default: () => ({})
}
})
//注册model const { tableContext } = useListPage({
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
//注册table数据
const { prefixCls, tableContext } = useListPage({
tableProps: { tableProps: {
title: '计划执行审批', title: '计划执行审批',
api: list, api: list,
columns, columns,
canResize: true, canResize: true,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
},
actionColumn: {
width: 200,
fixed: 'right',
}, },
actionColumn: { width: 200, fixed: 'right' },
}, },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handleExecuteApproval(record: Recordable) { function handleExecuteApproval(record: Recordable) {
emit("openMultiForm",record) emit('openMultiForm', record);
} }
function handleSuccess() { function handleSendNext(record: Recordable) {
(selectedRowKeys.value = []) && reload(); emit('sendWorkFlow', record);
} }
function handleSendNext(record: Recordable) { function handleSuccess() {
emit("sendWorkFlow",record) selectedRowKeys.value = [];
reload();
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [
{ { label: '整改审核', onClick: handleExecuteApproval.bind(null, record) },
label: '整改审核', { label: '提交', onClick: handleSendNext.bind(null, record) },
onClick: handleExecuteApproval.bind(null, record),
},
{
label: '提交',
onClick: handleSendNext.bind(null, record),
},
]; ];
} }
async function handleUpdate(dataId) { async function handleUpdate(dataId) {
await saveOrUpdate({ await saveOrUpdate({ bpmNodeId: props.nextFlowNode?.id, id: dataId }, true);
bmpNodeId: props.nextFlowNode.id,
id:dataId,
},true).then(res => {
handleSuccess(); handleSuccess();
})
} }
defineExpose({ defineExpose({ handleUpdate });
handleUpdate,
})
</script> </script>
<style scoped></style> <style scoped></style>
<template> <template>
<div> <div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题--> <template #tableTitle></template>
<template #tableTitle>
</template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)"/> <TableAction :actions="getTableAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal @register="registerExecuteModal" @success="handleSuccess" />
</div> </div>
</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 { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns } from './StProblemCheck.data';
import { list, saveOrUpdate } from './StProblemCheck.api'; import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: { type: Object, default: () => ({}) },
type: Object, currentFlowNode: { type: Object, default: () => ({}) },
default: () => ({}) nextFlowNode: { type: Object, default: () => ({}) },
}, });
currentFlowNode: {
type: Object, const emit = defineEmits(['callback', 'sendWorkFlow', 'openMultiForm']);
default: () => ({})
},
nextFlowNode: {
type: Object,
default: () => ({})
}
})
//注册model const { tableContext } = useListPage({
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
//注册table数据
const { prefixCls, tableContext } = useListPage({
tableProps: { tableProps: {
title: '问题整改', title: '问题整改',
api: list, api: list,
columns, columns,
canResize: true, canResize: true,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
},
actionColumn: {
width: 200,
fixed: 'right',
}, },
actionColumn: { width: 200, fixed: 'right' },
}, },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handleExecute(record: Recordable) { function handleExecute(record: Recordable) {
emit("openMultiForm",record) emit('openMultiForm', record);
} }
function handleSuccess() { function handleSendNext(record: Recordable) {
(selectedRowKeys.value = []) && reload(); emit('sendWorkFlow', record);
} }
function handleSendNext(record: Recordable) { function handleSuccess() {
emit("sendWorkFlow",record) selectedRowKeys.value = [];
reload();
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [
{ { label: '整改执行', onClick: handleExecute.bind(null, record) },
label: '整改执行', { label: '提交', onClick: handleSendNext.bind(null, record) },
onClick: handleExecute.bind(null, record),
},
{
label: '提交',
onClick: handleSendNext.bind(null, record),
},
]; ];
} }
async function handleUpdate(dataId, flowNode) {
async function handleUpdate(dataId,flowNode) { const record = { bpmNodeId: flowNode.id, deployId: flowNode.deployId, bpmStatus: 2, id: dataId };
let record = { await saveOrUpdate(record, true);
bmpNodeId: flowNode.id,
deployId: flowNode.deployId,
bpmStatus: 2,
id:dataId
}
await saveOrUpdate(record,true).then(res => {
handleSuccess(); handleSuccess();
})
} }
defineExpose({ defineExpose({ handleUpdate });
handleUpdate,
})
</script> </script>
<style scoped></style> <style scoped></style>
...@@ -39,29 +39,22 @@ ...@@ -39,29 +39,22 @@
@form-data-update="handleMultiFormDataUpdate" @form-data-update="handleMultiFormDataUpdate"
width="90%" width="90%"
@success="handlSendSuccess" @success="handlSendSuccess"
@approval-success="handlApprovalSuccess"
@approval-fail="handlApprovalFail" @approval-fail="handlApprovalFail"
/> />
<task-assignee-drawer @register="registerAssigneeDrawer" @success="handlSendSuccess" @error="handleError" />
<task-assignee-drawer @register="registerAssigneeDrawer"
@success="handlSendSuccess"
@error="handleError"
/>
</div> </div>
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { ref, nextTick, onMounted, defineAsyncComponent, h } from 'vue'; import { ref, nextTick, onMounted, defineAsyncComponent, h } from 'vue';
import { getNodesByTableName } from '/@/components/Process/api/definition'; import { getNodesByTableName } from '/@/components/Process/api/definition';
import { definitionStart, definitionStartByDeployId,addMyTaskFlow } from "/@/components/Process/api/definition"; import { definitionStart, definitionStartByDeployId, addMyTaskFlow } from "/@/components/Process/api/definition";
import { todoListAll } from "/@/components/Process/api/todo"; import { todoListAll } from "/@/components/Process/api/todo";
import WorkFlowFormDrawer from '/@/views/common/WorkFlowFormDrawer.vue'; import WorkFlowFormDrawer from '/@/views/common/WorkFlowFormDrawer.vue';
import TaskAssigneeDrawer from '/@/views/common/TaskAssigneeDrawer.vue' import TaskAssigneeDrawer from '/@/views/common/TaskAssigneeDrawer.vue';
import { useDrawer } from '/@/components/Drawer'; import { useDrawer } from '/@/components/Drawer';
const [ registerAssigneeDrawer, { openDrawer: openAssigneeDrawer }] = useDrawer();
const formTableName = "st_problem_check"; const formTableName = "st_problem_check";
const workflowNodes = ref<any[]>([]); const workflowNodes = ref<any[]>([]);
const activeTab = ref(1); const activeTab = ref(1);
...@@ -71,19 +64,10 @@ ...@@ -71,19 +64,10 @@
const deployId = ref(''); const deployId = ref('');
const procInsId = ref(''); const procInsId = ref('');
const taskId = ref(''); const taskId = ref('');
// 改为动态 ref 对象,存储每个节点的组件实例
const formComponentRefs = ref<Map<number, any>>(new Map()); const formComponentRefs = ref<Map<number, any>>(new Map());
// 任务指派抽屉相关状态
const drawerTaskVisible = ref(false);
const assignee = ref<any>(""); const assignee = ref<any>("");
const userType = ref<any>(""); const userType = ref<any>("");
const formData = ref<any>({});
const customNextApi = ref<any>(null);
const todoList = ref<any[]>([]); const todoList = ref<any[]>([]);
// 抽屉相关状态
const drawerVisible = ref(false); const drawerVisible = ref(false);
const drawerTitle = ref('表单处理'); const drawerTitle = ref('表单处理');
const currentMultiFormIndex = ref(0); const currentMultiFormIndex = ref(0);
...@@ -92,19 +76,15 @@ ...@@ -92,19 +76,15 @@
const componentCache = new Map(); const componentCache = new Map();
const modules = import.meta.glob('@/views/**/*.vue'); const modules = import.meta.glob('@/views/**/*.vue');
// 动态设置 ref 的函数 const [registerAssigneeDrawer, { openDrawer: openAssigneeDrawer }] = useDrawer();
function setFormComponentRef(el: any, index: number) { function setFormComponentRef(el: any, index: number) {
if (el) { if (el) formComponentRefs.value.set(index, el);
formComponentRefs.value.set(index, el); else formComponentRefs.value.delete(index);
} else {
formComponentRefs.value.delete(index);
}
} }
// 获取当前活动标签页对应的组件实例
function getCurrentFormComponent() { function getCurrentFormComponent() {
const currentIndex = activeTab.value - 1; return formComponentRefs.value.get(activeTab.value - 1);
return formComponentRefs.value.get(currentIndex);
} }
function handleTabChange(key) { function handleTabChange(key) {
...@@ -114,89 +94,62 @@ ...@@ -114,89 +94,62 @@
} }
function loadComponent(url: string) { function loadComponent(url: string) {
if (componentCache.has(url)) { if (componentCache.has(url)) return componentCache.get(url);
return componentCache.get(url);
}
let componentPath = '';
if (url.includes('/views')) {
componentPath = `/src${url}`;
} else {
componentPath = `/src/views${url}`;
}
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
componentPath += '.vue';
}
let componentPath = url.includes('/views') ? `/src${url}` : `/src/views${url}`;
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) componentPath += '.vue';
let loader = modules[componentPath]; const loader = modules[componentPath];
if (!loader) { if (!loader) {
const ErrorComponent = { const ErrorComponent = { render: () => h('div', { style: 'color: red; padding: 20px;' }, `组件未找到: ${componentPath}`) };
render: () => h('div', { style: 'color: red; padding: 20px;' }, `组件未找到: ${componentPath}`)
};
componentCache.set(url, ErrorComponent); componentCache.set(url, ErrorComponent);
return ErrorComponent; return ErrorComponent;
} }
const AsyncComponent = defineAsyncComponent({ const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>, loader: () => loader() as Promise<{ default: any }>,
loadingComponent: { loadingComponent: { render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...') },
render: () => h('div', { style: 'text-align: center; padding: 20px;' }, '加载中...') errorComponent: { render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败,请刷新页面重试') },
},
errorComponent: {
render: () => h('div', { style: 'color: red; padding: 20px;' }, '组件加载失败,请刷新页面重试')
},
delay: 200, delay: 200,
timeout: 3000, timeout: 3000,
onError(error) { onError(error) { console.error('异步组件加载错误:', error); }
console.error('异步组件加载错误:', error);
}
}); });
componentCache.set(url, AsyncComponent); componentCache.set(url, AsyncComponent);
return AsyncComponent; return AsyncComponent;
} }
const handleDefinitionStart = async (data) => { const handleDefinitionStart = async (data) => {
const formData = { dataId: data.id, dataName: 'id' };
const formData = { dataId:data.id, dataName: 'id' }; const nodeDeployId = currentNode.value.deployId || '';
const deployId = currentNode.value.deployId || ''; const startResRaw = await definitionStartByDeployId(nodeDeployId, formData);
const startResRaw = await definitionStartByDeployId(deployId, formData);
let myTaskFlow = {}
if (startResRaw?.instanceId) { if (startResRaw?.instanceId) {
procInsId.value = startResRaw.instanceId; procInsId.value = startResRaw.instanceId;
taskId.value = startResRaw.taskId; taskId.value = startResRaw.taskId;
myTaskFlow["taskId"] = startResRaw.taskId; const myTaskFlow = {
myTaskFlow["deployId"] = startResRaw.deployId; taskId: startResRaw.taskId,
myTaskFlow["procInsId"] = startResRaw.instanceId; deployId: startResRaw.deployId,
myTaskFlow["executionId"] = startResRaw.executionId; procInsId: startResRaw.instanceId,
myTaskFlow["targetId"] = data.id; executionId: startResRaw.executionId,
myTaskFlow["taskDefinitionKey"] = currentNode.value.id; targetId: data.id,
myTaskFlow["formTableName"] = formTableName; taskDefinitionKey: currentNode.value.id,
const attributes = currentNode.value?.attributes || {} formTableName: formTableName,
};
const attributes = currentNode.value?.attributes || {};
const userTypes = attributes.userType || []; const userTypes = attributes.userType || [];
if (userTypes.length > 0) { if (userTypes.length > 0) {
userType.value = userTypes[0].value || ''; userType.value = userTypes[0].value || '';
if(userType.value==="role"){ if (userType.value === "role") myTaskFlow["roleid"] = currentNode.value.assignee;
myTaskFlow["roleid"] = currentNode.value.assignee; else myTaskFlow["uid"] = currentNode.value.assignee;
} else {
myTaskFlow["uid"] = currentNode.value.assignee;;
}
} }
await addMyTaskFlow(myTaskFlow); await addMyTaskFlow(myTaskFlow);
const currentFormComponent = getCurrentFormComponent(); const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') { if (currentFormComponent?.handleStartUpdate) {
currentFormComponent.handleStartUpdate({ currentFormComponent.handleStartUpdate({ dataId: data.id, procInsId: procInsId.value });
dataId:data.id,
procInsId: procInsId.value,
});
} else {
console.warn('当前组件实例不存在或没有 handleUpdate 方法');
}
} }
} }
};
const handleDefinitionSend = async (data) => { const handleDefinitionSend = async (data) => {
drawerTaskVisible.value = true;
dataId.value = data.id; dataId.value = data.id;
deployId.value = currentNode.value.deployId || ''; deployId.value = currentNode.value.deployId || '';
await setNextNodeUser(); await setNextNodeUser();
...@@ -206,131 +159,94 @@ ...@@ -206,131 +159,94 @@
userType: userType.value, userType: userType.value,
dataId: data.id, dataId: data.id,
deployId: deployId.value, deployId: deployId.value,
}) });
};
}
const setNextNodeUser = async () => { const setNextNodeUser = async () => {
const nextNode = workflowNodes.value[activeTab.value]; const nextNode = workflowNodes.value[activeTab.value];
const attributes = nextNode.attributes || {}; const attributes = nextNode?.attributes || {};
const userTypes = attributes.userType || []; const userTypes = attributes.userType || [];
if (userTypes.length > 0) { if (userTypes.length > 0) {
userType.value = userTypes[0].value || ''; userType.value = userTypes[0].value || '';
if(userType.value==="role"){ if (userType.value === "role") {
if(nextNode.candidateGroups&&nextNode.candidateGroups.length>0){ assignee.value = nextNode.candidateGroups?.[0]?.id;
assignee.value = nextNode.candidateGroups[0].id;
}
} else { } else {
assignee.value = nextNode.assignee; assignee.value = nextNode.assignee;
} }
} }
} };
const handleMultiFormSubmit = async (submitData: {
nodeId: string;nodeName: string;formData: any;procDefId: string; const handleMultiFormSubmit = async (submitData: { nodeId: string; nodeName: string; formData: any; procDefId: string; }) => {
}) => {
try { try {
await definitionStart( await definitionStart(currentProcDefId.value, { ...submitData.formData });
currentProcDefId.value,{
...submitData.formData
}
);
drawerVisible.value = false; drawerVisible.value = false;
const currentComponent = loadComponent(workflowNodes.value[ activeTab.value - 1]?.formListUrl);
} catch (error) { } catch (error) {
console.error('提交失败:', error); console.error('提交失败:', error);
throw error; throw error;
} }
}; };
const handleDrawerClose = () => {
drawerVisible.value = false;
console.log('抽屉关闭');
};
const handleMultiFormDataUpdate = (data: any) => { const handleDrawerClose = () => { drawerVisible.value = false; };
console.log('多表单数据更新:', data); const handleMultiFormDataUpdate = (_data: any) => {};
// 可以在这里实时保存数据到本地
};
function handlSendSuccess(dataId: any) { function handlSendSuccess(dataId: any) {
const currentFormComponent = getCurrentFormComponent(); const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') { if (currentFormComponent?.handleUpdate) {
const nextNode = workflowNodes.value[currentMultiFormIndex.value+1]; const nextNode = workflowNodes.value[currentMultiFormIndex.value + 1];
currentFormComponent.handleUpdate(dataId,nextNode); currentFormComponent.handleUpdate(dataId, nextNode);
} else {
console.warn('当前组件实例不存在或没有 handleUpdate 方法');
} }
} }
function handleError(error: any) { function handleError(error: any) { console.error('任务处理失败:', error); }
console.error('任务处理失败:', error)
async function handlApprovalFail(dataId: any) {
const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent?.handleUpdate) {
const beforeNode = workflowNodes.value[currentMultiFormIndex.value - 1];
await currentFormComponent.handleUpdate(dataId, beforeNode);
}
} }
async function handlApprovalFail (dataId: any) { function handlApprovalSuccess(dataId: any) {
const currentFormComponent = getCurrentFormComponent(); const currentFormComponent = getCurrentFormComponent();
if (currentFormComponent && typeof currentFormComponent.handleUpdate === 'function') { if (currentFormComponent?.handleUpdate) {
const beforeNode = workflowNodes.value[currentMultiFormIndex.value-1]; const nextNode = workflowNodes.value[currentMultiFormIndex.value + 1];
await currentFormComponent.handleUpdate(dataId,beforeNode); currentFormComponent.handleUpdate(dataId, nextNode);
} else {
console.warn('当前组件实例不存在或没有 handleUpdate 方法');
} }
} }
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 || '表单处理'; 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 || '';
const isApprovalNode = JSON.parse(currentNode.value["isApprove"]); const isApprovalNode = JSON.parse(currentNode.value["isApprove"]);
isShowApprovalPanel.value = isApprovalNode;
if(isApprovalNode) {
isShowApprovalPanel.value = true;
} else {
isShowApprovalPanel.value = false;
}
await setNextNodeUser(); await setNextNodeUser();
} }
onMounted(async () => {
onMounted( async () => {
await nextTick(); await nextTick();
try { try {
const nodes = await getNodesByTableName(formTableName); const nodes = await getNodesByTableName(formTableName);
todoList.value = await todoListAll(); todoList.value = await todoListAll();
workflowNodes.value = nodes; workflowNodes.value = nodes;
if (workflowNodes.value && workflowNodes.value.length > 0) { if (workflowNodes.value?.length > 0) {
workflowNodes.value.forEach(node => { workflowNodes.value.forEach(node => {
if (node.formListUrl) { if (node.formListUrl) loadComponent(node.formListUrl);
loadComponent(node.formListUrl);
}
}); });
}
currentNode.value = workflowNodes.value[0]; currentNode.value = workflowNodes.value[0];
}
} catch (error) { } catch (error) {
console.error('获取工作流节点失败:', error); console.error('获取工作流节点失败:', error);
} }
}); });
</script> </script>
<style scoped> <style scoped>
.tab-content { .tab-content { padding: 0px; }
padding: 0px; .no-form { padding: 40px; text-align: center; color: #999; }
}
.no-form {
padding: 40px;
text-align: center;
color: #999;
}
</style> </style>
<template> <template>
<div> <div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题--> <template #tableTitle></template>
<template #tableTitle>
</template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)"/> <TableAction :actions="getTableAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal @register="registerExecuteModal" @success="handleSuccess" />
</div> </div>
</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 { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns } from './StProblemCheck.data';
import { list, saveOrUpdate } from './StProblemCheck.api'; import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: { type: Object, default: () => ({}) },
type: Object, currentFlowNode: { type: Object, default: () => ({}) },
default: () => ({}) nextFlowNode: { type: Object, default: () => ({}) },
}, });
currentFlowNode: {
type: Object,
default: () => ({})
},
nextFlowNode: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm']) const emit = defineEmits(['callback', 'sendWorkFlow', 'openMultiForm']);
//注册model const { tableContext } = useListPage({
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
//注册table数据
const { prefixCls, tableContext } = useListPage({
tableProps: { tableProps: {
title: '问题整改', title: '问题整改',
api: list, api: list,
columns, columns,
canResize: false, canResize: false,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
},
actionColumn: {
width: 200,
fixed: 'right',
}, },
actionColumn: { width: 200, fixed: 'right' },
}, },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handlePlanApproval(record: Recordable) { function handlePlanApproval(record: Recordable) {
emit("openMultiForm",record) emit('openMultiForm', record);
} }
function handleSuccess() { function handleSendNext(record: Recordable) {
(selectedRowKeys.value = []) && reload(); emit('sendWorkFlow', record);
} }
function handleSendNext(record: Recordable) { function handleSuccess() {
emit("sendWorkFlow",record) selectedRowKeys.value = [];
reload();
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [
{ { label: '计划审核', onClick: handlePlanApproval.bind(null, record) },
label: '计划审核',
onClick: handlePlanApproval.bind(null, record),
},
// {
// label: '提交',
// onClick: handleSendNext.bind(null, record),
// },
]; ];
} }
async function handleUpdate(dataId,flowNode) { async function handleUpdate(dataId, flowNode) {
let record = { const record = { bpmNodeId: flowNode.id, deployId: flowNode.deployId, bpmStatus: 2, id: dataId };
bmpNodeId: flowNode.id, await saveOrUpdate(record, true);
deployId: flowNode.deployId,
bpmStatus: 2,
id:dataId
}
await saveOrUpdate(record,true).then(res => {
handleSuccess(); handleSuccess();
})
} }
defineExpose({ defineExpose({ handleUpdate });
handleUpdate,
})
</script> </script>
<style scoped></style> <style scoped></style>
<template> <template>
<div> <div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题--> <template #tableTitle></template>
<template #tableTitle>
</template>
<!--操作栏-->
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="getTableAction(record)"/> <TableAction :actions="getTableAction(record)" />
</template> </template>
</BasicTable> </BasicTable>
<!-- 整改执行 -->
<StProblemCheckExecuteModal @register="registerExecuteModal" @success="handleSuccess" />
</div> </div>
</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 { useListPage } from '/@/hooks/system/useListPage'; import { useListPage } from '/@/hooks/system/useListPage';
import { columns, searchFormSchema } from './StProblemCheck.data'; import { columns } from './StProblemCheck.data';
import { list,saveOrUpdate} from './StProblemCheck.api'; import { list, saveOrUpdate } from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback','sendWorkFlow','openMultiForm'])
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: { type: Object, default: () => ({}) },
type: Object, currentFlowNode: { type: Object, default: () => ({}) },
default: () => ({}) nextFlowNode: { type: Object, default: () => ({}) },
}, });
currentFlowNode: {
type: Object,
default: () => ({})
},
nextFlowNode: {
type: Object,
default: () => ({})
}
})
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal(); const emit = defineEmits(['callback', 'sendWorkFlow', 'openMultiForm']);
const { prefixCls, tableContext } = useListPage({
const { tableContext } = useListPage({
tableProps: { tableProps: {
title: '问题整改计划', title: '问题整改计划',
api: list, api: list,
columns, columns,
canResize: false, canResize: false,
beforeFetch(params) { beforeFetch(params) {
params['bmpNodeId'] = props.currentFlowNode.id params['bpmNodeId'] = props.currentFlowNode.id;
},
actionColumn: {
width: 200,
fixed: 'right',
}, },
actionColumn: { width: 200, fixed: 'right' },
}, },
}); });
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handlePlan(record: Recordable) { function handlePlan(record: Recordable) {
emit("openMultiForm",record) emit('openMultiForm', record);
} }
async function handleSendNext(record: Recordable) { function handleSendNext(record: Recordable) {
emit("sendWorkFlow",record) emit('sendWorkFlow', record);
} }
function handleSuccess() { function handleSuccess() {
(selectedRowKeys.value = []) && reload(); selectedRowKeys.value = [];
reload();
} }
function getTableAction(record) { function getTableAction(record) {
return [ return [
{ { label: '制定计划', onClick: handlePlan.bind(null, record) },
label: '制定计划', { label: '提交', onClick: handleSendNext.bind(null, record) },
onClick: handlePlan.bind(null, record),
},
{
label: '提交',
onClick: handleSendNext.bind(null, record),
},
]; ];
} }
async function handleUpdate(dataId,flowNode) { async function handleUpdate(dataId, flowNode) {
let record = { const record = { bpmNodeId: flowNode.id, deployId: flowNode.deployId, bpmStatus: 2, id: dataId };
bmpNodeId: flowNode.id, await saveOrUpdate(record, true);
deployId: flowNode.deployId,
bpmStatus: 2,
id:dataId
}
await saveOrUpdate(record,true).then(res => {
handleSuccess(); handleSuccess();
})
} }
defineExpose({ defineExpose({ handleUpdate });
handleUpdate,
})
</script> </script>
<style scoped></style> <style scoped></style>
<template>
<div>
<vxe-drawer
show-confirm-button
show-cancel-button
v-model="showPopup"
:title="pageTilte"
width="100%"
height="100%"
:loading="loading"
>
<iframe
id="iframeId"
ref="iframeRef"
:src="frmUrl"
frameborder="0"
style="width: 100%; height: 100%;"
></iframe>
</vxe-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted,nextTick } from "vue";
import { useUserStoreWithOut } from "/@/store/modules/user";
import { defHttp } from '/@/utils/http/axios';
import { getToken } from '/@/utils/auth';
const user = useUserStoreWithOut();
const showPopup = ref(false);
const frmUrl = ref("");
const loading = ref(false);
const pageTilte = ref("");
const iframeRef = ref<HTMLIFrameElement>();
const iniPage = async (data) => {
pageTilte.value = data.projectName;
showPopup.value = true;
frmUrl.value = `${import.meta.env.VITE_APP_JFLOW_CORE_ADDR}/#/WF/MyFlow?FlowNo=102&Token=${user.getJflowToken}&tid=${data.id}`;
const setSourctUrl = '/api/jflow/setCCWorkId';
await defHttp.get({
url: setSourctUrl,
params: {"targetId":data.id,"targetKey":"targetKey","token":getToken()},
});
}
// 暴露方法
defineExpose({ iniPage });
</script>
...@@ -38,32 +38,17 @@ ...@@ -38,32 +38,17 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue'; import { ref, reactive, onMounted } from 'vue';
import { defHttp } from '/@/utils/http/axios'; import { defHttp } from '/@/utils/http/axios';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
import { getValueType } from '/@/utils';
import { Form } from 'ant-design-vue'; import { Form } from 'ant-design-vue';
import { useTabs } from '/@/hooks/web/useTabs';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import { useUserStoreWithOut } from '/@/store/modules/user';
import { useRouter } from 'vue-router';
import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
import { TOKEN_KEY } from '/@/enums/cacheEnum';
import { useUserStore } from '/@/store/modules/user';
const router = useRouter();
const user = useUserStoreWithOut();
const { closeCurrent, refreshPage, setTitle } = useTabs();
const props = defineProps({ const props = defineProps({
formDisabled: { type: Boolean, default: false }, formDisabled: { type: Boolean, default: false },
formData: { type: Object, default: () => {} }, formData: { type: Object, default: () => ({}) },
formBpm: { type: Boolean, default: true }, formBpm: { type: Boolean, default: true },
}); });
const formRef = ref();
const useForm = Form.useForm;
const emit = defineEmits(['register', 'ok']); const emit = defineEmits(['register', 'ok']);
const formData = reactive<Record<string, any>>({ const formData = reactive<Record<string, any>>({
id: '', id: '',
...@@ -72,65 +57,36 @@ ...@@ -72,65 +57,36 @@
problemSource: '', problemSource: '',
isReceive: '', isReceive: '',
programme: '', programme: '',
labels: '',
actIds: '',
}); });
const { createMessage } = useMessage(); const { createMessage } = useMessage();
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } }); const labelCol = ref({ xs: { span: 24 }, sm: { span: 5 } });
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } }); const wrapperCol = ref({ xs: { span: 24 }, sm: { span: 16 } });
const confirmLoading = ref<boolean>(false); const confirmLoading = ref(false);
const loading = ref(false); const disabled = ref(true);
//表单验证
const validatorRules = {};
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
const disabled = ref<boolean>(true);
const listAll = '/problem/stProblemCheck/queryById'; const listAll = '/problem/stProblemCheck/queryById';
// 从URL获取参数
const getUrlParams = () => { const getUrlParams = () => {
const winurl = window.location.href; const winurl = window.location.href;
const params = new URLSearchParams(winurl.split('?')[1]); const params = new URLSearchParams(winurl.split('?')[1]);
return { return { cctoken: params.get('Token') || '', WorkID: params.get('WorkID') || '' };
cctoken: params.get('Token') || '',
WorkID: params.get('WorkID') || '',
};
}; };
const iniData = async () => { const iniData = async () => {
try { try {
loading.value = true; confirmLoading.value = true;
const { cctoken, WorkID } = getUrlParams(); const { cctoken, WorkID } = getUrlParams();
console.log('Token:', cctoken, 'WorkID:', WorkID);
const timestamp = new Date().getTime(); const timestamp = new Date().getTime();
const gettokeyUrl = '/api/jflow/getCCWorkTokenAndTid'; const gettokeyUrl = '/api/jflow/getCCWorkTokenAndTid';
const { tid, token } = await defHttp.get({ const { tid, token } = await defHttp.get({
url: gettokeyUrl, url: gettokeyUrl,
params: { params: { targetKey: 'targetKey', flowToken: cctoken, WorkID: WorkID, _t: timestamp },
targetKey: 'targetKey',
flowToken: cctoken,
WorkID: WorkID,
_t: timestamp,
},
}); });
console.log('问题整改tid ', tid);
// setAuthCache(TOKEN_KEY, token);
//params: { id: tid, token: token, "_t": timestamp },
defHttp.get({ url: listAll, params: { token: token, id: tid } }).then((res) => {
console.log('res ', res);
defHttp.get({ url: listAll, params: { token, id: tid } }).then((res) => {
formData.problemItem = res.problemItem; formData.problemItem = res.problemItem;
formData.problemDes = res.problemDes; formData.problemDes = res.problemDes;
formData.problemSource = res.problemSource; formData.problemSource = res.problemSource;
if (res.isReceive == '1') { formData.isReceive = res.isReceive == '1' ? '是' : '否';
formData.isReceive = '是';
} else {
formData.isReceive = '否';
}
formData.programme = res.programme; formData.programme = res.programme;
formData.strategy = res.strategy; formData.strategy = res.strategy;
}); });
...@@ -138,21 +94,18 @@ ...@@ -138,21 +94,18 @@
console.error('初始化表单数据失败:', error); console.error('初始化表单数据失败:', error);
createMessage.error('初始化表单数据失败'); createMessage.error('初始化表单数据失败');
} finally { } finally {
loading.value = false; confirmLoading.value = false;
} }
}; };
onMounted(async () => { onMounted(() => { iniData(); });
iniData();
});
defineExpose({}); defineExpose({});
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.antd-modal-form { .antd-modal-form {
min-height: 500px !important; min-height: 500px !important;
overflow-y: auto; overflow-y: auto;
padding: 24px 24px 24px 24px; padding: 24px;
} }
</style> </style>
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
try { try {
let values = await validate(); let values = await validate();
values.bpmStatus = bpmFlowData.value?.bpmStatus || ''; values.bpmStatus = bpmFlowData.value?.bpmStatus || '';
values.bmpNodeId = bpmFlowData.value?.bmpNodeId || ''; values.bpmNodeId = bpmFlowData.value?.bpmNodeId || '';
values.deployId = bpmFlowData.value?.deployId || ''; values.deployId = bpmFlowData.value?.deployId || '';
setModalProps({ confirmLoading: true }); setModalProps({ confirmLoading: true });
//提交表单 //提交表单
......
...@@ -65,12 +65,11 @@ ...@@ -65,12 +65,11 @@
} }
// 保存按钮 // 保存按钮
async function saveForm() { async function saveForm(formData) {
try { try {
let data = getFieldsValue();
let params = Object.assign({}, currentFormData.value, data);
const result = await saveOrUpdate(params, true);
const result = await saveOrUpdate(formData, true);
alert(JSON.stringify(result))
// 保存成功后更新表单 // 保存成功后更新表单
if (result && result.id) { if (result && result.id) {
await initFormData(result.id); await initFormData(result.id);
......
<template>
<BasicModal
v-bind="$attrs"
@register="registerModal"
destroyOnClose
:title="modalTitle"
:width="modalWidth"
:fullscreen="true"
@ok="handleSubmit"
class="problem-check-plan-modal"
>
<div class="plan-modal-content">
<a-row :gutter="16" class="plan-content-wrapper">
<!-- 左侧:问题内容展示区域 -->
<a-col :xs="24" :sm="24" :md="12" :lg="10" :xl="9" class="problem-display-section">
<div class="problem-display-card">
<div class="section-header">
<Icon icon="carbon:warning-alt" class="header-icon" />
<span class="header-title">问题详情</span>
</div>
<div class="problem-content">
<div class="problem-item">
<div class="item-label">问题编号:</div>
<div class="item-value">{{ problemData.problemNo || '-' }}</div>
</div>
<div class="problem-item">
<div class="item-label">项目分类:</div>
<div class="item-value">{{ problemData.projectCategory || '-' }}</div>
</div>
<div class="problem-item">
<div class="item-label">问题来源:</div>
<div class="item-value">{{ problemData.problemSource || '-' }}</div>
</div>
<div class="problem-item">
<div class="item-label">所属领域:</div>
<div class="item-value">{{ problemData.domain || '-' }}</div>
</div>
<div class="problem-item">
<div class="item-label">风险等级:</div>
<div class="item-value">
<a-tag :color="getRiskLevelColor(problemData.riskLevel)">{{ problemData.riskLevel || '-' }}</a-tag>
</div>
</div>
<div class="problem-item">
<div class="item-label">严重程度:</div>
<div class="item-value">
<a-tag :color="getSeverityColor(problemData.severity)">{{ problemData.severity || '-' }}</a-tag>
</div>
</div>
<div class="problem-item problem-des">
<div class="item-label">问题描述:</div>
<div class="item-value">{{ problemData.problemDes || '-' }}</div>
</div>
</div>
</div>
</a-col>
<!-- 右侧:计划表单区域 -->
<a-col :xs="24" :sm="24" :md="12" :lg="14" :xl="15" class="plan-form-section">
<div class="plan-form-card">
<div class="section-header">
<Icon icon="carbon:task-approved" class="header-icon" />
<span class="header-title">制定计划</span>
</div>
<BasicForm @register="registerForm" class="plan-form">
<template #jSelectUser="{ model, field }">
<JSelectUser v-model:value="model[field]" />
</template>
</BasicForm>
</div>
</a-col>
</a-row>
</div>
<template #footer>
<a-button key="back" @click="handleCancel">取消</a-button>
<a-button key="submit" type="primary" @click="handleSubmit" :loading="confirmLoading">确定</a-button>
</template>
</BasicModal>
</template>
<script lang="ts" setup>
import { ref, computed, unref, watch } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { Icon } from '/@/components/Icon';
import { JSelectUser } from '/@/components/Form';
import { planFormSchema } from '../StProblemCheck.data';
import { saveOrUpdate } from '../StProblemCheck.api';
import { message } from 'ant-design-vue';
// Emits声明
const emit = defineEmits(['register', 'success']);
// 状态变量
const isUpdate = ref(true);
const confirmLoading = ref(false);
const problemData = ref<any>({});
const modalWidth = ref('80%');
const bpmStatus = ref('');
// 动态标题
const modalTitle = computed(() => '制定计划');
// 响应式宽度调整
watch(
() => window.innerWidth,
(width) => {
if (width < 768) {
modalWidth.value = '100%';
} else if (width < 1200) {
modalWidth.value = '90%';
} else {
modalWidth.value = '80%';
}
},
{ immediate: true }
);
// 增强版表单配置 - 将整改方案改为富文本编辑器
const enhancedPlanFormSchema = [
...planFormSchema.map(schema => {
// 将整改方案字段改为富文本编辑器
if (schema.field === 'rectifyPlan') {
return {
...schema,
component: 'JEditor',
componentProps: {
height: 350,
placeholder: '请输入详细的整改方案...',
},
};
}
return schema;
}),
];
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
//labelWidth: 150,
schemas: enhancedPlanFormSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
await resetFields();
bpmStatus.value = data?.bpmStatus || '';
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
if (unref(isUpdate)) {
//表单赋值
problemData.value = { ...data.record };
await setFieldsValue({
...data.record,
});
} else {
// 新增模式
problemData.value = data.record ? { ...data.record } : {};
await setFieldsValue({
bpmStatus: bpmStatus.value,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter });
});
// 获取风险等级颜色
function getRiskLevelColor(level: string) {
const colorMap: Record<string, string> = {
'高': 'red',
'中': 'orange',
'低': 'green',
};
return colorMap[level] || 'blue';
}
// 获取严重程度颜色
function getSeverityColor(severity: string) {
const colorMap: Record<string, string> = {
'严重': 'red',
'一般': 'orange',
'轻微': 'green',
};
return colorMap[severity] || 'blue';
}
//取消按钮
function handleCancel() {
closeModal();
}
//表单提交事件
async function handleSubmit(v) {
try {
let values = await validate();
setModalProps({ confirmLoading: true });
// 构建提交数据
const submitData = {
...values,
problemNo: problemData.value.problemNo,
projectCategory: problemData.value.projectCategory,
domain: problemData.value.domain,
};
//提交表单
await saveOrUpdate(submitData, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
message.success('计划保存成功!');
} catch (error: any) {
console.error('提交失败:', error);
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
.problem-check-plan-modal {
:deep(.ant-modal-body) {
padding: 0;
max-height: calc(100vh - 120px);
overflow-y: auto;
}
}
.plan-modal-content {
padding: 20px;
min-height: calc(100vh - 180px);
}
.plan-content-wrapper {
height: 100%;
}
// 左侧问题展示区域
.problem-display-section {
height: 100%;
margin-bottom: 16px;
@media (min-width: 768px) {
margin-bottom: 0;
}
}
.problem-display-card {
background: #f5f7fa;
border-radius: 8px;
padding: 20px;
height: 100%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
// 右侧表单区域
.plan-form-section {
height: 100%;
}
.plan-form-card {
background: #fff;
border-radius: 8px;
padding: 20px;
height: 100%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
// 区域标题
.section-header {
display: flex;
align-items: center;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #e8e8e8;
.header-icon {
font-size: 20px;
margin-right: 10px;
color: #1890ff;
}
.header-title {
font-size: 16px;
font-weight: 600;
color: #262626;
}
}
// 问题内容样式
.problem-content {
.problem-item {
margin-bottom: 16px;
display: flex;
.item-label {
width: 100px;
flex-shrink: 0;
font-weight: 500;
color: #595959;
font-size: 14px;
line-height: 1.6;
}
.item-value {
flex: 1;
color: #262626;
font-size: 14px;
line-height: 1.6;
word-break: break-word;
}
&.problem-des {
.item-value {
white-space: pre-wrap;
}
}
}
}
// 表单样式
.plan-form {
:deep(.ant-form-item) {
margin-bottom: 20px;
}
:deep(.ant-form-item-label) {
label {
font-weight: 500;
}
}
}
// 响应式调整
@media (max-width: 767px) {
.plan-modal-content {
padding: 12px;
}
.problem-display-card,
.plan-form-card {
padding: 16px;
}
.section-header {
margin-bottom: 16px;
.header-title {
font-size: 15px;
}
}
.problem-content {
.problem-item {
flex-direction: column;
.item-label {
width: auto;
margin-bottom: 4px;
}
}
}
}
@media (min-width: 768px) and (max-width: 1023px) {
.plan-modal-content {
padding: 16px;
}
}
// 滚动条美化
.plan-modal-content {
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-thumb {
background: #bfbfbf;
border-radius: 4px;
&:hover {
background: #999;
}
}
&::-webkit-scrollbar-track {
background: #f0f0f0;
}
}
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
\ No newline at end of file
import { ref, computed } from 'vue';
import { useListPage } from '/@/hooks/system/useListPage';
import { list, saveOrUpdate } from '../StProblemCheck.api';
import { columns } from '../StProblemCheck.data';
export function useProblemList(props) {
const { tableContext, reload } = useListPage({
tableProps: {
title: props?.title || '问题整改',
api: list,
columns,
canResize: props?.canResize ?? false,
beforeFetch(params) {
if (props?.currentFlowNode?.id) {
params['bpmNodeId'] = props.currentFlowNode.id;
}
if (props?.todoList) {
params['todoList'] = props.todoList;
}
},
actionColumn: {
width: 200,
fixed: 'right',
},
},
exportConfig: {
name: '问题整改',
url: '/problem/stProblemCheck/exportXls',
},
importConfig: {
url: '/problem/stProblemCheck/importExcel',
},
});
const [registerTable, , { rowSelection, selectedRowKeys }] = tableContext;
async function handleUpdate(dataId, flowNode) {
const record = {
bpmNodeId: flowNode.id,
deployId: flowNode.deployId,
bpmStatus: 2,
id: dataId,
};
await saveOrUpdate(record, true);
selectedRowKeys.value = [];
reload();
}
function handleSuccess() {
selectedRowKeys.value = [];
reload();
}
return {
registerTable,
reload,
rowSelection,
selectedRowKeys,
handleUpdate,
handleSuccess,
};
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论