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

完善问题

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