提交 4996e66f authored 作者: kxjia's avatar kxjia

问题管理

上级 4fa74ba6
...@@ -14,6 +14,7 @@ export const listDefinition = async (params) => { ...@@ -14,6 +14,7 @@ export const listDefinition = async (params) => {
// 部署流程实例 // 部署流程实例
export function definitionStart(procDefId, data) { export function definitionStart(procDefId, data) {
alert(JSON.stringify(procDefId))
return defHttp.post({ return defHttp.post({
url: '/flowable/definition/startByProcDefId', url: '/flowable/definition/startByProcDefId',
data: { data: {
......
...@@ -10,7 +10,6 @@ export function myProcessList(query) { ...@@ -10,7 +10,6 @@ export function myProcessList(query) {
} }
export function flowFormData(query) { export function flowFormData(query) {
alert(JSON.stringify(query))
return defHttp.get({ return defHttp.get({
url: '/flowable/task/flowFormData', url: '/flowable/task/flowFormData',
params: query params: query
......
<template>
<div class="multi-form-preview">
<!-- 只读表单列表:前 N-1 个 -->
<div v-for="(node, idx) in previousNodes" :key="`readonly-${idx}`" class="form-readonly-item">
<div class="form-header">
<span class="form-name">{{ node.name }}</span>
<a-tag color="blue" class="readonly-tag">只读</a-tag>
</div>
<div class="form-content readonly">
<component
:is="loadComponent(node.formUrl)"
:disabled="true"
:readonly="true"
:form-data="formDataMap[node.id]"
:current-flow-node="node"
/>
</div>
</div>
<!-- 可编辑表单:第 N 个(当前节点) -->
<div v-if="currentNode" class="form-editable-item">
<div class="form-header">
<span class="form-name">{{ currentNode.name }}</span>
<a-tag color="green">可编辑</a-tag>
</div>
<div class="form-content editable">
<component
:is="loadComponent(currentNode.formUrl)"
:disabled="false"
:readonly="false"
:form-data="currentFormData"
:current-flow-node="currentNode"
@update:form-data="handleFormDataUpdate"
@submit="handleSubmit"
/>
</div>
</div>
<!-- 加载错误提示 -->
<a-empty v-if="!currentNode && previousNodes.length === 0" description="未找到有效表单节点" />
</div>
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, defineAsyncComponent, h, watch } from 'vue';
import { message } from 'ant-design-vue';
// 定义组件属性
const props = defineProps({
// 当前节点在 workflowNodes 中的索引(从 0 开始)
currentNodeIndex: {
type: Number,
required: true,
default: 2 // 默认第三个(索引2)
},
// 工作流节点列表(从父组件传入)
workflowNodes: {
type: Array as () => Array<any>,
required: true,
default: () => []
},
// 外部传入的表单数据(用于回显)
externalFormData: {
type: Object,
default: () => ({})
}
});
// 定义事件
const emit = defineEmits(['form-data-update', 'submit']);
// 组件缓存
const componentCache = new Map();
// 使用 import.meta.glob 预加载所有可能的组件
const modules = import.meta.glob('@/views/**/*.vue');
// 当前节点的表单数据(可编辑)
const currentFormData = ref<any>({});
// 只读节点的表单数据映射(按节点id存储)
const formDataMap = ref<Record<string, any>>({});
// 获取前 N 个节点(只读部分)
const previousNodes = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) return [];
const idx = props.currentNodeIndex;
// 取索引小于 idx 的节点(即当前节点之前的所有节点)
return props.workflowNodes.slice(0, idx);
});
// 获取当前节点(第 N 个,可编辑)
const currentNode = computed(() => {
if (!props.workflowNodes || props.workflowNodes.length === 0) return null;
const idx = props.currentNodeIndex;
if (idx < 0 || idx >= props.workflowNodes.length) return null;
return props.workflowNodes[idx];
});
// 将 URL 转换为正确的导入路径并动态加载组件
function loadComponent(url: string) {
if (!url) {
console.warn('formUrl 为空');
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';
}
console.log('加载组件路径:', componentPath);
// 从预加载的 modules 中获取加载器
let loader = modules[componentPath];
if (!loader) {
console.error('未找到组件:', componentPath);
console.log('可用路径示例:', Object.keys(modules).slice(0, 5));
const ErrorComponent = createErrorComponent(`组件未找到: ${componentPath}`);
componentCache.set(url, ErrorComponent);
return ErrorComponent;
}
// 创建异步组件,并注入额外的 props(disabled/readonly 等)
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,
onError(error) {
console.error('异步组件加载错误:', error);
}
});
componentCache.set(url, AsyncComponent);
return AsyncComponent;
}
// 创建空组件(当 formUrl 为空时使用)
function createEmptyComponent() {
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)
};
}
// 处理表单数据更新(来自可编辑组件)
function handleFormDataUpdate(data: any) {
currentFormData.value = { ...currentFormData.value, ...data };
emit('form-data-update', currentFormData.value);
}
// 处理提交事件
function handleSubmit(data: any) {
emit('submit', {
nodeId: currentNode.value?.id,
nodeName: currentNode.value?.name,
formData: data || currentFormData.value
});
}
// 初始化只读节点的表单数据(从外部传入或模拟)
function initReadonlyFormData() {
// 如果外部传入了表单数据,则按节点id映射
if (props.externalFormData && Object.keys(props.externalFormData).length > 0) {
// 假设 externalFormData 的结构为 { nodeId: formData, ... }
previousNodes.value.forEach(node => {
if (props.externalFormData[node.id]) {
formDataMap.value[node.id] = props.externalFormData[node.id];
} else {
// 如果没有传入,设置空对象
formDataMap.value[node.id] = {};
}
});
} else {
// 初始化空数据
previousNodes.value.forEach(node => {
formDataMap.value[node.id] = {};
});
}
}
// 监听外部表单数据变化
watch(() => props.externalFormData, (newData) => {
if (newData && Object.keys(newData).length > 0) {
previousNodes.value.forEach(node => {
if (newData[node.id]) {
formDataMap.value[node.id] = newData[node.id];
}
});
// 如果当前节点有数据,也更新
if (currentNode.value && newData[currentNode.value.id]) {
currentFormData.value = newData[currentNode.value.id];
}
}
}, { deep: true, immediate: true });
// 组件挂载时初始化
onMounted(() => {
initReadonlyFormData();
console.log('MultiFormPreview 组件已挂载');
console.log('当前节点索引:', props.currentNodeIndex);
console.log('只读节点数量:', previousNodes.value.length);
console.log('当前节点:', currentNode.value?.name);
});
</script>
<style scoped lang="scss">
.multi-form-preview {
width: 100%;
padding: 16px;
background-color: #f5f7fa;
border-radius: 8px;
.form-readonly-item,
.form-editable-item {
margin-bottom: 24px;
background-color: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s;
&:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.form-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background-color: #fafbfc;
border-bottom: 1px solid #e8eef2;
.form-name {
font-size: 15px;
font-weight: 500;
color: #1f2f3d;
}
.readonly-tag,
.ant-tag {
font-size: 12px;
}
}
.form-content {
padding: 20px;
&.readonly {
background-color: #fefefe;
// 只读模式下添加半透明遮罩效果,但仍保留交互(如果子组件支持disabled)
opacity: 0.95;
}
&.editable {
background-color: #fff;
}
}
}
.form-editable-item {
border: 1px solid #d9ecff;
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.08);
.form-header {
background-color: #e6f7ff;
border-bottom-color: #bae7ff;
.form-name {
color: #096dd9;
}
}
}
}
</style>
\ No newline at end of file
...@@ -183,12 +183,13 @@ export const formSchema: FormSchema[] = [ ...@@ -183,12 +183,13 @@ export const formSchema: FormSchema[] = [
{ {
label: '问题描述', label: '问题描述',
field: 'problemDes', field: 'problemDes',
component: 'InputTextArea', component: 'JEditor',
required: true, required: true,
componentProps: { componentProps: {
rows: 4, rows: 4,
showCount: true, showCount: true,
maxlength: 3000, maxlength: 30000,
height: 300,
}, },
}, },
{ {
......
...@@ -2,10 +2,8 @@ ...@@ -2,10 +2,8 @@
<div> <div>
<BasicTable @register="registerTable" :rowSelection="rowSelection"> <BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle> <template #tableTitle>
<a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined">添加问题</a-button>
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0"> <a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay> <template #overlay>
<a-menu> <a-menu>
...@@ -55,6 +53,8 @@ ...@@ -55,6 +53,8 @@
} }
}) })
const emit = defineEmits(['callback'])
//注册model //注册model
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerPlanModal, { openModal: openPlanModal }] = useModal(); const [registerPlanModal, { openModal: openPlanModal }] = useModal();
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
/** /**
* 新增事件 * 新增事件
*/ */
function handleAdd() { function handleAdd333333() {
openModal(true, { openModal(true, {
isUpdate: false, isUpdate: false,
showFooter: true, showFooter: true,
...@@ -106,6 +106,10 @@ ...@@ -106,6 +106,10 @@
} }
}); });
} }
function handleAdd() {
emit("callback",null)
}
/** /**
* 编辑事件 * 编辑事件
*/ */
...@@ -162,7 +166,7 @@ ...@@ -162,7 +166,7 @@
onClick: handleEdit.bind(null, record), onClick: handleEdit.bind(null, record),
}, },
{ {
label: '启动流程', label: '发送任务',
onClick: handleFlow.bind(null, record), onClick: handleFlow.bind(null, record),
}, },
......
...@@ -4,34 +4,60 @@ ...@@ -4,34 +4,60 @@
<a-tab-pane v-for="(node, index) in workflowNodes" :key="index + 1" :tab="node.name"> <a-tab-pane v-for="(node, index) in workflowNodes" :key="index + 1" :tab="node.name">
<div v-if="node.formListUrl" class="tab-content"> <div v-if="node.formListUrl" class="tab-content">
<component :is="loadComponent(node.formListUrl)" <component :is="loadComponent(node.formListUrl)"
:beforeFlowNode="workflowNodes[index-1]" :currentFlowNode="node" :nextFlowNode="workflowNodes[index+1]" /> @startWorkFlow="handleDefinitionStart"
:beforeFlowNode="workflowNodes[index-1]"
:currentFlowNode="node"
:nextFlowNode="workflowNodes[index+1]"
@open-multi-form="handleOpenMultiForm"
@callback="handleCallback"
/>
</div> </div>
<div v-else class="no-form"> <div v-else class="no-form">
该节点未配置表单 该节点未配置表单
</div> </div>
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
<!-- 多表单抽屉组件 -->
<WorkFlowFormDrawer
v-model:visible="drawerVisible"
:title="drawerTitle"
:current-node-index="currentMultiFormIndex"
:workflow-nodes="workflowNodes"
:external-form-data="externalFormData"
:proc-def-id="currentProcDefId"
@submit="handleMultiFormSubmit"
@close="handleDrawerClose"
@form-data-update="handleMultiFormDataUpdate"
width="50%"
/>
</div> </div>
</template> </template>
<script lang="ts" name="problem-stProblemCheck" setup> <script lang="ts" name="problem-stProblemCheck" setup>
import { ref, nextTick, onMounted, defineAsyncComponent, h } from 'vue'; import { ref, nextTick, onMounted, defineAsyncComponent, h } from 'vue';
import { getNodesByTableName } from '/@/components/Process/api/definition'; import { getNodesByTableName } from '/@/components/Process/api/definition';
import { definitionStart, flowXmlAndNode } from "/@/components/Process/api/definition";
import WorkFlowFormDrawer from '/@/views/common/WorkFlowFormDrawer.vue';
const workflowNodes = ref<any[]>([]); const workflowNodes = ref<any[]>([]);
const activeTab = ref(1); const activeTab = ref(1);
// 组件缓存,避免重复加载 // 抽屉相关状态
const componentCache = new Map(); const drawerVisible = ref(false);
const drawerTitle = ref('表单处理');
const currentMultiFormIndex = ref(0);
const currentProcDefId = ref('');
const externalFormData = ref<Record<string, any>>({});
// 使用 import.meta.glob 预加载所有可能的组件,使用 /@/ 格式 const componentCache = new Map();
const modules = import.meta.glob('@/views/**/*.vue'); const modules = import.meta.glob('@/views/**/*.vue');
function handleTabChange(key) { function handleTabChange(key) {
activeTab.value = key; activeTab.value = key;
currentMultiFormIndex.value = key - 1;
} }
// 将URL转换为正确的导入路径并动态加载组件
function loadComponent(url: string) { function loadComponent(url: string) {
console.log('开始加载组件,URL:', url); console.log('开始加载组件,URL:', url);
...@@ -39,44 +65,29 @@ ...@@ -39,44 +65,29 @@
console.log('从缓存加载组件:', url); console.log('从缓存加载组件:', url);
return componentCache.get(url); return componentCache.get(url);
} }
// 构建使用 /@/ 格式的组件路径
let componentPath = ''; let componentPath = '';
// 检查url是否已包含/views
if (url.includes('/views')) { if (url.includes('/views')) {
// 如果已包含/views,直接使用 /@/ 前缀
componentPath = `/src${url}`; componentPath = `/src${url}`;
} else { } else {
// 否则添加 /@/views 前缀
componentPath = `/src/views${url}`; componentPath = `/src/views${url}`;
} }
// 检查是否已有文件后缀
if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) { if (!componentPath.match(/\.(vue|js|ts|jsx|tsx)$/)) {
// 如果没有文件后缀,添加.vue
componentPath += '.vue'; componentPath += '.vue';
} }
console.log('生成的组件路径:', componentPath); console.log('生成的组件路径:', componentPath);
console.log('所有可用的组件路径示例:', Object.keys(modules).slice(0, 10)); console.log('所有可用的组件路径示例:', Object.keys(modules).slice(0, 10));
// 从预加载的 modules 中获取加载器
let loader = modules[componentPath]; let loader = modules[componentPath];
if (!loader) { if (!loader) {
console.error('未找到组件:', componentPath); console.error('未找到组件:', componentPath);
console.log('包含 problemCheck 的路径:', Object.keys(modules).filter(key => key.includes('problemCheck'))); console.log('包含 problemCheck 的路径:', Object.keys(modules).filter(key => key.includes('problemCheck')));
// 返回错误组件
const ErrorComponent = { const ErrorComponent = {
render: () => h('div', { style: 'color: red; padding: 20px;' }, `组件未找到: ${componentPath}`) render: () => h('div', { style: 'color: red; padding: 20px;' }, `组件未找到: ${componentPath}`)
}; };
componentCache.set(url, ErrorComponent); componentCache.set(url, ErrorComponent);
return ErrorComponent; return ErrorComponent;
} }
// 创建异步组件
const AsyncComponent = defineAsyncComponent({ const AsyncComponent = defineAsyncComponent({
loader: () => loader() as Promise<{ default: any }>, loader: () => loader() as Promise<{ default: any }>,
loadingComponent: { loadingComponent: {
...@@ -91,11 +102,89 @@ ...@@ -91,11 +102,89 @@
console.error('异步组件加载错误:', error); console.error('异步组件加载错误:', error);
} }
}); });
componentCache.set(url, AsyncComponent); componentCache.set(url, AsyncComponent);
return AsyncComponent; return AsyncComponent;
} }
const handleDefinitionStart = (procDefId: string, submitData: any) => {
return definitionStart({
procDefId,
variables: submitData
})
}
const handleOpenMultiForm = (params: {
nodeIndex?: number;
title?: string;
procDefId?: string;
formData?: Record<string, any>;
}) => {
console.log('打开多表单抽屉:', params);
if (params.nodeIndex !== undefined) {
currentMultiFormIndex.value = params.nodeIndex;
} else {
currentMultiFormIndex.value = activeTab.value - 1;
}
if (params.title) {
drawerTitle.value = params.title;
} else {
const currentNode = workflowNodes.value[currentMultiFormIndex.value];
drawerTitle.value = currentNode ? `${currentNode.name} - 历史表单查看` : '表单处理';
}
if (params.procDefId) {
currentProcDefId.value = params.procDefId;
} else if (workflowNodes.value[currentMultiFormIndex.value]) {
currentProcDefId.value = workflowNodes.value[currentMultiFormIndex.value].procDefId || '';
}
if (params.formData) {
externalFormData.value = params.formData;
} else {
externalFormData.value = {};
}
drawerVisible.value = true;
};
const handleMultiFormSubmit = async (submitData: {
nodeId: string;
nodeName: string;
formData: any;
procDefId: string;
}) => {
console.log('多表单提交数据:', submitData);
try {
await definitionStart({
procDefId: submitData.procDefId,
variables: submitData.formData
});
drawerVisible.value = false;
const currentTabKey = activeTab.value;
const currentComponent = loadComponent(workflowNodes.value[currentTabKey - 1]?.formListUrl);
} catch (error) {
console.error('提交失败:', error);
throw error;
}
};
const handleDrawerClose = () => {
drawerVisible.value = false;
console.log('抽屉关闭');
};
const handleMultiFormDataUpdate = (data: any) => {
console.log('多表单数据更新:', data);
// 可以在这里实时保存数据到本地
};
const openHistoryForms = (nodeIndex: number, formData?: Record<string, any>) => {
handleOpenMultiForm({
nodeIndex,
title: `查看历史表单 - ${workflowNodes.value[nodeIndex]?.name || ''}`,
formData
});
};
defineExpose({
openHistoryForms,
openMultiForm: handleOpenMultiForm
});
onMounted(async () => { onMounted(async () => {
await nextTick(); await nextTick();
try { try {
...@@ -104,12 +193,10 @@ ...@@ -104,12 +193,10 @@
workflowNodes.value = nodes; workflowNodes.value = nodes;
console.log('获取到的工作流节点:', workflowNodes.value); console.log('获取到的工作流节点:', workflowNodes.value);
// 打印每个节点的 formListUrl
workflowNodes.value.forEach((node, index) => { workflowNodes.value.forEach((node, index) => {
console.log(`节点${index + 1}:`, node.name, 'formListUrl:', node.formListUrl); console.log(`节点${index + 1}:`, node.name, 'formListUrl:', node.formListUrl);
}); });
// 预加载所有节点的组件
if (workflowNodes.value && workflowNodes.value.length > 0) { if (workflowNodes.value && workflowNodes.value.length > 0) {
console.log('开始预加载组件...'); console.log('开始预加载组件...');
workflowNodes.value.forEach(node => { workflowNodes.value.forEach(node => {
...@@ -123,6 +210,22 @@ ...@@ -123,6 +210,22 @@
console.error('获取工作流节点失败:', error); console.error('获取工作流节点失败:', error);
} }
}); });
function handleCallback(data: any) {
drawerVisible.value = true;
const currentNode = workflowNodes.value[currentMultiFormIndex.value];
currentProcDefId.value = currentNode.procDefId || '';
const userid = currentNode.assignee || '';
const nodeId = currentNode.id
const deployId = currentNode.deployId || '';
const procDefId = currentNode.procDefId || '';
const attributes = currentNode.attributes || {};
const userType = attributes.userType || [];
if (userType.length > 0) {
data['userType'] = userType[0].value || '';
}
}
</script> </script>
<style scoped> <style scoped>
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
import { list} from './StProblemCheck.api'; import { list} from './StProblemCheck.api';
import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue'; import StProblemCheckExecuteModal from './components/StProblemCheckExecuteModal.vue';
const emit = defineEmits(['callback'])
const props = defineProps({ const props = defineProps({
beforeFlowNode: { beforeFlowNode: {
...@@ -41,9 +42,7 @@ ...@@ -41,9 +42,7 @@
} }
}) })
const [registerExecuteModal, { openModal: openExecuteModal }] = useModal(); const [registerExecuteModal, { openModal: openExecuteModal }] = useModal();
const { prefixCls, tableContext } = useListPage({ const { prefixCls, tableContext } = useListPage({
tableProps: { tableProps: {
title: '问题整改计划', title: '问题整改计划',
...@@ -71,12 +70,7 @@ ...@@ -71,12 +70,7 @@
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext; const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
function handlePlan(record: Recordable) { function handlePlan(record: Recordable) {
openExecuteModal(true, { emit("callback",record)
record,
isUpdate: true,
showFooter: true,
nextFlowNode: props.nextFlowNode,
});
} }
function handleSuccess() { function handleSuccess() {
......
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
let formData = {}; let formData = {};
const queryByIdUrl = '/problem/stProblemCheck/queryById/'; const queryByIdUrl = '/problem/stProblemCheck/queryById/';
alert(props.formData.dataId)
async function initFormData() { async function initFormData() {
let params = { id: props.formData.dataId }; let params = { id: props.formData.dataId };
const data = await defHttp.get({ url: queryByIdUrl, params }); const data = await defHttp.get({ url: queryByIdUrl, params });
...@@ -53,6 +52,7 @@ ...@@ -53,6 +52,7 @@
} }
async function submitForm() { async function submitForm() {
alert(888)
let data = getFieldsValue(); let data = getFieldsValue();
let params = Object.assign({}, formData, data); let params = Object.assign({}, formData, data);
console.log('表单数据', params); console.log('表单数据', params);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论