Browse Source

解决无法保存流程的问题

master
18226920803 1 month ago
parent
commit
869f939f01
  1. 2
      smart-flow-web/src/router/flow/flow.js
  2. 522
      smart-flow-web/src/views/flow/definition/definition-form.vue
  3. 16
      smart-flow-web/src/views/flow/definition/definition-list.vue
  4. 7
      smart-flow-web/src/views/flow/definition/warm-flow.vue

2
smart-flow-web/src/router/flow/flow.js

@ -7,7 +7,7 @@ export const flowRouters = [
permissions: ['flow:definition:design'], permissions: ['flow:definition:design'],
children: [ children: [
{ {
path: 'index/:id(\\d+)', path: 'index/:id',
component: () => import('/@/views/flow/definition/warm-flow.vue'), component: () => import('/@/views/flow/definition/warm-flow.vue'),
name: 'Design', name: 'Design',
meta: { title: '流程设计', activeMenu: '/flow/definition' } meta: { title: '流程设计', activeMenu: '/flow/definition' }

522
smart-flow-web/src/views/flow/definition/definition-form.vue

@ -1,262 +1,374 @@
<template> <template>
<a-modal <a-modal
:title="form.positionId ? '编辑' : '添加'" :title="title"
width="600px" :open="visible"
:open="visibleFlag" :width="1000"
@cancel="onClose" @ok="handleSubmit"
:maskClosable="false" @cancel="handleCancel"
:destroyOnClose="true" :confirmLoading="submitLoading"
forceRender
>
<a-form ref="formRef" :model="form" :rules="rules" :label-col="{ span: 6 }">
<a-tabs v-model:activeKey="tabsValue">
<a-tab-pane key="1" tab="基础设置"/>
<a-tab-pane key="2" tab="监听器" force-render/>
</a-tabs>
<div v-show="tabsValue === '1'">
<a-form-item label="流程编码" name="flowCode">
<a-input style="width: 100%" v-model:value="form.flowCode" placeholder="流程编码" />
</a-form-item>
<a-form-item label="流程名称" name="flowName">
<a-input style="width: 100%" v-model:value="form.flowName" placeholder="流程名称" />
</a-form-item>
<a-form-item label="流程类别" name="category">
<a-input style="width: 100%" v-model:value="form.category" placeholder="流程类别" />
</a-form-item>
<a-form-item label="审批表单" name="formCustom">
<a-select v-model:value="form.formCustom" style="width: 100%" :allowClear="true">
<a-select-option value="N">表单路径</a-select-option>
</a-select>
</a-form-item>
<a-form-item label="审批表单路径" name="formPath" v-if="form.formCustom === 'N'">
<a-input style="width: 100%" v-model:value="form.formPath" placeholder="请输入审批表单路径" />
</a-form-item>
</div>
<div v-show="tabsValue === '2'">
<a-form-item name="listenerRows" class="listenerItem">
<a-table
:dataSource="form.listenerRows"
:columns="columns"
:pagination="false"
size="small"
> >
<template #bodyCell="{ column, record, index }"> <el-form ref="formRef" :model="form" class="dialogForm" :rules="rules" label-width="150px" :disabled="disabled">
<template v-if="column.dataIndex === 'listenerType'"> <el-form-item label="流程编码" prop="flowCode">
<a-form-item <el-input v-model="form.flowCode" placeholder="请输入流程编码" maxlength="40" show-word-limit />
:name="['listenerRows', index, 'listenerType']" </el-form-item>
:rules="rules.listenerType"
>
<a-select
v-model:value="record.listenerType"
placeholder="请选择类型"
>
<a-select-option value="start">开始</a-select-option>
<a-select-option value="assignment">分派</a-select-option>
<a-select-option value="finish">完成</a-select-option>
<a-select-option value="create">创建</a-select-option>
</a-select>
</a-form-item>
</template>
<template v-if="column.dataIndex === 'listenerPath'"> <el-form-item label="流程名称" prop="flowName">
<a-form-item <el-input v-model="form.flowName" placeholder="请输入流程名称" maxlength="100" show-word-limit @input="nameChange" />
:name="['listenerRows', index, 'listenerPath']" </el-form-item>
:rules="rules.listenerPath"
> <el-form-item label="设计器模型" prop="modelValue">
<a-input <el-radio-group v-model="form.modelValue" :disabled="!!definitionId" @change="modelValueChange">
v-model:value="record.listenerPath" <el-radio label="CLASSICS">经典模型</el-radio>
placeholder="请输入路径" <el-radio label="MIMIC">仿钉钉模型
/> <span style="color: #ff4949; margin-left: 50px;">切换后重置节点保存后不支持修改</span>
</a-form-item> </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="流程类别" prop="category">
<el-tree-select
v-model="form.category"
:data="categoryList"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择流程类别"
check-strictly/>
</el-form-item>
<el-form-item label="自定义表单" prop="formCustom">
<el-radio-group v-model="form.formCustom">
<el-radio label="N">
<span class="flex-hc">
<el-tooltip class="box-item" effect="dark" placement="top"
content="填写页面地址:如system/process/approve">
<el-icon :size="14" class="ml5">
<WarningFilled />
</el-icon>
</el-tooltip>
</span>
</el-radio>
<el-radio label="Y">
<span class="flex-hc">
<el-tooltip class="box-item" effect="dark" placement="top"
content="填写自定义表单的唯一标识:如formCode+version">
<el-icon :size="14" class="ml5">
<WarningFilled />
</el-icon>
</el-tooltip>
</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="表单路径" prop="formPath" v-if="form.formCustom === 'N'">
<el-input v-model="form.formPath" placeholder="请输入审批表单路径" maxlength="100" show-word-limit/>
</el-form-item>
<el-form-item label="自定义表单唯一标识" prop="formPath" v-else-if="form.formCustom === 'Y'">
<el-tree-select
v-model="form.formPath"
:data="formPathList"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
placeholder="请选择流程类别"
check-strictly/>
</el-form-item>
<el-form-item prop="listenerRows" class="listenerItem">
<el-table v-if="form.listenerRows && form.listenerRows.length > 0" :key="form.listenerRows.length" :data="form.listenerRows" style="width: 100%">
<el-table-column prop="listenerType" width="150" label="类型">
<template #default="scope">
<el-form-item :prop="`listenerRows.${scope?.$index}.listenerType`" :rules="rules.listenerType">
<el-select v-model="scope.row.listenerType" placeholder="请选择类型">
<el-option label="开始" value="start"></el-option>
<el-option label="分派" value="assignment"></el-option>
<el-option label="完成" value="finish"></el-option>
<el-option label="创建" value="create"></el-option>
</el-select>
</el-form-item>
</template> </template>
</el-table-column>
<template v-if="column.dataIndex === 'action'"> <el-table-column prop="listenerPath" label="路径">
<a-button <template #default="scope">
type="link" <el-form-item :prop="`listenerRows.${scope?.$index}.listenerPath`" :rules="rules.listenerPath">
danger <el-input v-model="scope.row.listenerPath" placeholder="请输入路径" />
size="small" </el-form-item>
@click="handleDeleteRow(index)"
>
<template #icon><delete-outlined /></template>
</a-button>
</template> </template>
</el-table-column>
<el-table-column label="操作" width="65" v-if="!disabled">
<template #default="scope">
<el-button size="small" type="danger" icon="Delete" @click="handleDeleteRow(scope?.$index)" />
</template> </template>
</a-table> </el-table-column>
</el-table>
<el-button v-if="!disabled" style="margin-top: 10px;" type="primary" @click="handleAddRow">增加行</el-button>
</el-form-item>
</el-form>
</a-modal>
<a-button <a-modal title="流程设计"
type="primary" :open="designVisible"
style="margin-top: 10px" width="90%"
@click="handleAddRow" :footer="null"
> @cancel="closeDesign">
增加行 <div style="height: 80vh" class="iframe-wrapper">
</a-button> <iframe :src="designIframeUrl" style="width: 100%; height: 100%" frameborder="0" scrolling="no" class="custom-iframe" />
</a-form-item>
</div> </div>
</a-form>
<template #footer>
<a-space>
<a-button @click="onClose">取消</a-button>
<a-button type="primary" @click="onSubmit">保存</a-button>
</a-space>
</template>
</a-modal> </a-modal>
</template> </template>
<script setup>
import { reactive, ref, nextTick } from 'vue'; <script setup name="BaseInfo">
import _ from 'lodash'; import { ref, getCurrentInstance, watch } from "vue";
import { useRouter } from "vue-router";
import { WarningFilled } from "@element-plus/icons-vue";
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { definitionApi } from '/@/api/flow/definition-api'; import { definitionApi } from '/@/api/flow/definition-api';
import { SmartLoading } from '/@/components/framework/smart-loading';
import { smartSentry } from '/@/lib/smart-sentry'; import { smartSentry } from '/@/lib/smart-sentry';
import { useUserStore } from "/@/store/modules/system/user.js";
// ------------------------ ------------------------ const router = useRouter();
const { proxy } = getCurrentInstance();
const emits = defineEmits(['reloadList']); const emit = defineEmits(['reloadList']);
// ------------------------ ------------------------
//
const visibleFlag = ref(false);
//
const tabsValue = ref('1');
const columns = [ const props = defineProps({
{ disabled: {
title: '类型', type: Boolean,
dataIndex: 'listenerType', default: false
width: 150,
}, },
{ logicJson: {
title: '路径', type: Object,
dataIndex: 'listenerPath', default () {
return {}
}
}, },
{ categoryList: {
title: '操作', type: Array,
dataIndex: 'action', default () {
width: 65, return []
}
}, },
]; formPathList: {
type: Array,
function handleAddRow() { default () {
form.listenerRows.push({ return []
listenerType: undefined, //
listenerPath: '' //
});
} }
function handleDeleteRow(index) { },
form.listenerRows.splice(index, 1); definitionId: {
type: String,
default () {
return null
} }
},
});
const visible = ref(false);
const title = ref('新增流程定义');
const submitLoading = ref(false);
const designVisible = ref(false);
const designIframeUrl = ref("");
const form = ref({
id: null,
flowCode: "",
flowName: "",
modelValue: "",
category: "",
formCustom: "N",
formPath: "",
listenerType: "",
listenerPath: "",
listenerRows: []
});
function show(rowData) { watch(() => props.logicJson, newValue => {
Object.assign(form, formDefault); if (newValue && Object.keys(newValue).length > 0) {
if (rowData && !_.isEmpty(rowData)) { Object.assign(form.value, newValue);
Object.assign(form, rowData); setListenerData();
} }
if (rowData.listenerType) { });
const listenerTypes = rowData.listenerType.split(",");
const listenerPaths = rowData.listenerPath.split("@@"); const rules = {
form.listenerRows = listenerTypes.map((type, index) => ({ modelValue: [
{ required: true, message: "设计器模型不能为空", trigger: "blur" }
],
flowCode: [
{ required: true, message: "流程编码不能为空", trigger: "blur" }
],
flowName: [
{ required: true, message: "流程名称不能为空", trigger: "blur" }
],
formCustom: [
{ required: true, message: "请选择审批表单是否自定义", trigger: "change" }
],
listenerType: [
{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }
],
listenerPath: [
{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }
]
};
const formRef = ref();
function setListenerData() {
if (form.value.listenerType) {
const listenerTypes = form.value.listenerType.split(",");
const listenerPaths = form.value.listenerPath.split("@@");
form.value.listenerRows = listenerTypes.map((type, index) => ({
listenerType: type, listenerType: type,
listenerPath: listenerPaths[index] listenerPath: listenerPaths[index]
})); }));
} else { } else {
form.listenerRows = []; form.value.listenerRows = [];
} }
visibleFlag.value = true;
nextTick(() => {
formRef.value.clearValidate();
// ,
const domArr = document.getElementsByClassName('ant-modal');
if (domArr && domArr.length > 0) {
Array.from(domArr).forEach((item) => {
if (item.childNodes && item.childNodes.length > 0) {
Array.from(item.childNodes).forEach((child) => {
if (child.setAttribute) {
child.setAttribute('aria-hidden', 'false');
} }
});
function handleAddRow() {
form.value.listenerRows.push({ listenerType: "", listenerPath: "" });
formRef.value?.clearValidate("listenerRows");
} }
});
function handleDeleteRow(index) {
form.value.listenerRows.splice(index, 1);
} }
function validate() {
return new Promise((resolve) => {
proxy.$nextTick(() => {
proxy.$refs.formRef.validate(valid => {
resolve(valid);
});
});
}); });
} }
function onClose() { function nameChange(flowName) {
Object.assign(form, formDefault); proxy.$emit('update:flow-name', flowName);
visibleFlag.value = false;
} }
// ------------------------ ------------------------ function modelValueChange() {
proxy.$emit('update:model-value');
// ref }
const formRef = ref();
const formDefault = { function getFormData() {
id: undefined, form.value.listenerType = form.value.listenerRows.map(row => row.listenerType).join(",");
flowCode: undefined, form.value.listenerPath = form.value.listenerRows.map(row => row.listenerPath).join("@@");
flowName: undefined, return form.value;
category: undefined, }
formCustom: 'N',
formPath: undefined,
listenerRows: [], //
};
let form = reactive({ ...formDefault }); function show(data) {
if (data && data.id) {
title.value = '编辑流程定义';
Object.assign(form.value, data);
setListenerData();
visible.value = true;
} else {
router.push({ path: "/flow/flow-design/index/new" });
}
}
const rules = { function resetForm() {
flowCode: [{ required: true, message: '请输入流程编码' }], form.value = {
flowName: [{ required: true, message: '请输入流程名称' }], id: null,
formCustom: [{ required: true, message: '请选择审批表单' }], flowCode: "",
listenerType: [{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }], flowName: "",
listenerPath: [{ required: true, message: '监听器不能为空', trigger: ['change', 'blur'] }] modelValue: "",
category: "",
formCustom: "N",
formPath: "",
listenerType: "",
listenerPath: "",
listenerRows: []
}; };
//
async function onSubmit() {
try {
await formRef.value.validateFields();
form.listenerType = form.listenerRows.map(row => row.listenerType).join(",")
form.listenerPath = form.listenerRows.map(row => row.listenerPath).join("@@")
console.log(form)
save();
} catch (err) {
message.error('参数验证错误,请仔细填写表单数据!');
} }
function handleCancel() {
visible.value = false;
resetForm();
} }
// API async function handleSubmit() {
async function save() {
SmartLoading.show();
try { try {
if (form.id) { const valid = await validate();
await definitionApi.update(form); if (!valid) return;
submitLoading.value = true;
SmartLoading.show();
const formData = getFormData();
if (formData.id) {
await definitionApi.update(formData);
message.success('修改成功');
} else { } else {
await definitionApi.add(form); const result = await definitionApi.add(formData);
message.success('新增成功');
visible.value = false;
resetForm();
if (result.data && result.data.id) {
designIframeUrl.value = import.meta.env.VITE_APP_API_URL +
`/warm-flow-ui/index.html?id=${result.data.id}&disabled=false&Authorization=Bearer ` +
useUserStore().getToken +
'&clientid=' + import.meta.env.VITE_APP_CLIENT_ID;
designVisible.value = true;
}
return;
} }
message.success('操作成功');
emits('reloadList'); visible.value = false;
onClose(); resetForm();
} catch (err) { emit('reloadList');
smartSentry.captureError(err); } catch (e) {
smartSentry.captureError(e);
} finally { } finally {
submitLoading.value = false;
SmartLoading.hide(); SmartLoading.hide();
} }
} }
defineExpose({ function closeDesign() {
show, designVisible.value = false;
}); emit('reloadList');
}
defineExpose({ getFormData, validate, show });
</script> </script>
<style scoped> <style scoped>
/* 关键优化点:覆盖表单项和表格边距 */ :deep(.Tabs) {
.listenerItem :deep() .ant-form-item { margin-top: -20px;
margin-top: 5px !important; box-shadow: none;
margin-bottom: 5px !important; border-bottom: 0;
}
:deep(.Tabs .el-tabs__content) {
display: none;
}
:deep(.Tabs .el-tabs__item.is-active) {
margin-left: 0;
border-top: 1px solid var(--el-border-color);
margin-top: 0;
}
.dialogForm {
border-top: 0;
padding: 15px;
width: 1000px;
margin: 50px auto;
}
:deep(.listenerItem .el-form-item__label) {
float: none;
display: inline-block;
text-align: left;
} }
.listenerItem :deep() .ant-table { :deep(.listenerItem .el-form-item__content) {
margin-top: 5px !important; margin-left: 0 !important;
margin-bottom: 5px !important;
} }
</style> </style>

16
smart-flow-web/src/views/flow/definition/definition-list.vue

@ -218,6 +218,13 @@
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {useUserStore} from "/@/store/modules/system/user.js"; import {useUserStore} from "/@/store/modules/system/user.js";
// ---------------------------- / ----------------------------
const formRef = ref();
function showForm(data) {
formRef.value.show(data);
}
// ---------------------------- ---------------------------- // ---------------------------- ----------------------------
const columns = ref([ const columns = ref([
@ -318,14 +325,7 @@
onMounted(queryData); onMounted(queryData);
// ---------------------------- / ---------------------------- // ---------------------------- ----------------------------
const formRef = ref();
function showForm(data) {
formRef.value.show(data);
}
//
const upload = reactive({ const upload = reactive({
open: false, open: false,
isUploading: false, isUploading: false,

7
smart-flow-web/src/views/flow/definition/warm-flow.vue

@ -10,7 +10,12 @@ import {useUserStore} from "/@/store/modules/system/user.js";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
import {getCurrentInstance, onMounted, onUnmounted, ref} from 'vue'; import {getCurrentInstance, onMounted, onUnmounted, ref} from 'vue';
const iframeUrl = ref(import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?id=${proxy.$route.params.id}&disabled=${proxy.$route.query.disabled}&Authorization=Bearer ` + useUserStore().getToken); const routeId = proxy.$route.params.id;
const isNew = routeId === 'new';
const iframeUrl = ref(isNew
? import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?disabled=false&Authorization=Bearer ` + useUserStore().getToken
: import.meta.env.VITE_APP_API_URL + `/warm-flow-ui/index.html?id=${routeId}&disabled=false&Authorization=Bearer ` + useUserStore().getToken
);
onMounted(() => { onMounted(() => {
window.addEventListener("message", handleMessage); window.addEventListener("message", handleMessage);

Loading…
Cancel
Save