perf: bpm 流程模型表单代码优化. 去掉大量watch 代码. 用依赖注入替换props 传递流程数据. 增加导入导出

pull/655/MERGE
zws 2025-01-14 08:46:08 +08:00
parent 49fc9b5412
commit 3d8f1290c3
9 changed files with 156 additions and 729 deletions

View File

@ -40,7 +40,7 @@ defineOptions({
name: 'SimpleProcessDesigner' name: 'SimpleProcessDesigner'
}) })
const emits = defineEmits(['success', 'init-finished']) // const emits = defineEmits(['success']) //
const props = defineProps({ const props = defineProps({
modelId: { modelId: {
@ -59,13 +59,12 @@ const props = defineProps({
startUserIds : { startUserIds : {
type: Array, type: Array,
required: false required: false
},
value: {
type: [String, Object],
required: false
} }
}) })
const processData = inject('processData') as Ref
const loading = ref(false) const loading = ref(false)
const formFields = ref<string[]>([]) const formFields = ref<string[]>([])
const formType = ref(20) const formType = ref(20)
@ -76,9 +75,6 @@ const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
const deptTreeOptions = ref() const deptTreeOptions = ref()
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) //
//
const currentValue = ref<SimpleFlowNode | undefined>()
provide('formFields', formFields) provide('formFields', formFields)
provide('formType', formType) provide('formType', formType)
provide('roleList', roleOptions) provide('roleList', roleOptions)
@ -88,7 +84,8 @@ provide('deptList', deptOptions)
provide('userGroupList', userGroupOptions) provide('userGroupList', userGroupOptions)
provide('deptTree', deptTreeOptions) provide('deptTree', deptTreeOptions)
provide('startUserIds', props.startUserIds) provide('startUserIds', props.startUserIds)
provide('tasks', [])
provide('processInstance', {})
const message = useMessage() // const message = useMessage() //
const processNodeTree = ref<SimpleFlowNode | undefined>() const processNodeTree = ref<SimpleFlowNode | undefined>()
const errorDialogVisible = ref(false) const errorDialogVisible = ref(false)
@ -112,70 +109,14 @@ const updateModel = () => {
} }
} }
//
const loadProcessData = async (data: any) => {
try {
if (data) {
const parsedData = typeof data === 'string' ? JSON.parse(data) : data
processNodeTree.value = parsedData
currentValue.value = parsedData
//
await nextTick()
if (simpleProcessModelRef.value?.refresh) {
await simpleProcessModelRef.value.refresh()
}
}
} catch (error) {
console.error('加载流程数据失败:', error)
}
}
//
watch(
() => props.value,
async (newValue, oldValue) => {
if (newValue && newValue !== oldValue) {
await loadProcessData(newValue)
}
},
{ immediate: true, deep: true }
)
//
watch(
() => processNodeTree.value,
async (newValue, oldValue) => {
if (newValue && oldValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
await saveSimpleFlowModel(newValue)
}
},
{ deep: true }
)
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => { const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
if (!simpleModelNode) { if (!simpleModelNode) {
return return
} }
//
errorNodes = []
validateNode(simpleModelNode, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return
}
try { try {
if (props.modelId) { processData.value = simpleModelNode
//
const data = {
id: props.modelId,
simpleModel: simpleModelNode
}
await updateBpmSimpleModel(data)
}
//
currentValue.value = simpleModelNode
emits('success', simpleModelNode) emits('success', simpleModelNode)
} catch (error) { } catch (error) {
console.error('保存失败:', error) console.error('保存失败:', error)
@ -246,61 +187,20 @@ onMounted(async () => {
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id') deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
// //
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList() userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
// //
if (props.modelId) { if (processData.value) {
// SIMPLE processNodeTree.value = processData?.value
const result = await getBpmSimpleModel(props.modelId)
if (result) {
await loadProcessData(result)
} else {
updateModel()
}
} else if (props.value) {
await loadProcessData(props.value)
} else { } else {
updateModel() updateModel()
} }
} finally { } finally {
loading.value = false loading.value = false
emits('init-finished')
} }
}) })
const simpleProcessModelRef = ref() const simpleProcessModelRef = ref()
/** 获取当前流程数据 */
const getCurrentFlowData = async () => {
try {
if (simpleProcessModelRef.value) {
const data = await simpleProcessModelRef.value.getCurrentFlowData()
if (data) {
currentValue.value = data
return data
}
}
return currentValue.value
} catch (error) {
console.error('获取流程数据失败:', error)
return currentValue.value
}
}
//
const refresh = async () => {
try {
if (currentValue.value) {
await loadProcessData(currentValue.value)
}
} catch (error) {
console.error('刷新失败:', error)
}
}
defineExpose({ defineExpose({
getCurrentFlowData,
updateModel,
loadProcessData,
refresh
}) })
</script> </script>

View File

@ -3,11 +3,31 @@
<div class="position-absolute top-0px right-0px bg-#fff"> <div class="position-absolute top-0px right-0px bg-#fff">
<el-row type="flex" justify="end"> <el-row type="flex" justify="end">
<el-button-group key="scale-control" size="default"> <el-button-group key="scale-control" size="default">
<el-button size="default" @click="exportJson()"><Icon icon="ep:download" />导出</el-button>
<el-button size="default" @click="importJson()"><Icon icon="ep:upload" />导入</el-button>
<!-- 用于打开本地文件-->
<input
type="file"
id="files"
ref="refFile"
style="display: none"
accept=".json"
@change="importLocalFile"
/>
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" /> <el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" /> <el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button> <el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
<el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" /> <el-button size="default" :plain="true" :icon="ZoomIn" @click="zoomIn()" />
</el-button-group> </el-button-group>
<!-- <el-button-->
<!-- v-if="!readonly"-->
<!-- size="default"-->
<!-- class="ml-4px"-->
<!-- type="primary"-->
<!-- :icon="Select"-->
<!-- @click="saveSimpleFlowModel"-->
<!-- >保存模型</el-button-->
<!-- >-->
</el-row> </el-row>
</div> </div>
<div class="simple-process-model" :style="`transform: scale(${scaleValue / 100});`"> <div class="simple-process-model" :style="`transform: scale(${scaleValue / 100});`">
@ -33,7 +53,8 @@
import ProcessNodeTree from './ProcessNodeTree.vue' import ProcessNodeTree from './ProcessNodeTree.vue'
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts' import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
import { useWatchNode } from './node' import { useWatchNode } from './node'
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue' import { ZoomOut, ZoomIn, ScaleToOriginal, Select } from '@element-plus/icons-vue'
import { isString } from '@/utils/is'
defineOptions({ defineOptions({
name: 'SimpleProcessModel' name: 'SimpleProcessModel'
@ -85,6 +106,16 @@ const processReZoom = () => {
const errorDialogVisible = ref(false) const errorDialogVisible = ref(false)
let errorNodes: SimpleFlowNode[] = [] let errorNodes: SimpleFlowNode[] = []
const saveSimpleFlowModel = async () => {
errorNodes = []
validateNode(processNodeTree.value, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return
}
emits('save', processNodeTree.value)
}
// showText // showText
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => { const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
if (node) { if (node) {
@ -143,6 +174,36 @@ const getCurrentFlowData = async () => {
defineExpose({ defineExpose({
getCurrentFlowData getCurrentFlowData
}) })
const exportJson = () => {
const blob = new Blob([JSON.stringify(processNodeTree.value)]);
const tempLink = document.createElement('a'); // a
const href = window.URL.createObjectURL(blob); //
//filename
const fileName = `model.json`;
tempLink.href = href;
tempLink.target = '_blank';
tempLink.download = fileName;
document.body.appendChild(tempLink);
tempLink.click(); //
document.body.removeChild(tempLink); //
window.URL.revokeObjectURL(href); // blob
}
const importJson = () => {
refFile.value.click()
}
const refFile = ref()
//
const importLocalFile = () => {
const file = refFile.value.files[0]
const reader = new FileReader()
reader.readAsText(file)
reader.onload = function () {
if (isString(this.result)) {
processNodeTree.value = JSON.parse(this.result)
}
}
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -308,28 +308,6 @@ const props = defineProps({
} }
}) })
// value,
watch(
() => props.value,
(newValue) => {
if (newValue && bpmnModeler) {
createNewDiagram(newValue)
}
},
{ immediate: true }
)
// processIdprocessName
watch(
[() => props.processId, () => props.processName],
([newId, newName]) => {
if (newId && newName && !props.value) {
createNewDiagram(null)
}
},
{ immediate: true }
)
provide('configGlobal', props) provide('configGlobal', props)
let bpmnModeler: any = null let bpmnModeler: any = null
const defaultZoom = ref(1) const defaultZoom = ref(1)
@ -480,6 +458,7 @@ const initModelListeners = () => {
emit('commandStack-changed', event) emit('commandStack-changed', event)
emit('input', xml) emit('input', xml)
emit('change', xml) emit('change', xml)
emit('save', xml)
} catch (e: any) { } catch (e: any) {
console.error(`[Process Designer Warn]: ${e.message || e}`) console.error(`[Process Designer Warn]: ${e.message || e}`)
} }

View File

@ -3,6 +3,7 @@
<!-- 流程设计器负责绘制流程等 --> <!-- 流程设计器负责绘制流程等 -->
<MyProcessDesigner <MyProcessDesigner
key="designer" key="designer"
v-if="xmlString != undefined"
v-model="xmlString" v-model="xmlString"
:value="xmlString" :value="xmlString"
v-bind="controlForm" v-bind="controlForm"
@ -15,7 +16,7 @@
/> />
<!-- 流程属性器负责编辑每个流程节点的属性 --> <!-- 流程属性器负责编辑每个流程节点的属性 -->
<MyProcessPenal <MyProcessPenal
v-if="isModelerReady && modeler" v-if="modeler"
key="penal" key="penal"
:bpmnModeler="modeler" :bpmnModeler="modeler"
:prefix="controlForm.prefix" :prefix="controlForm.prefix"
@ -37,8 +38,8 @@ defineOptions({ name: 'BpmModelEditor' })
const props = defineProps<{ const props = defineProps<{
modelId?: string modelId?: string
modelKey?: string modelKey: string
modelName?: string modelName: string
value?: string value?: string
}>() }>()
@ -51,10 +52,11 @@ const formType = ref(20)
provide('formFields', formFields) provide('formFields', formFields)
provide('formType', formType) provide('formType', formType)
const xmlString = ref<string>('') // BPMN XML //
const xmlString = inject('processData') as Ref
const modeler = shallowRef() // BPMN Modeler const modeler = shallowRef() // BPMN Modeler
const processDesigner = ref() const processDesigner = ref()
const isModelerReady = ref(false)
const controlForm = ref({ const controlForm = ref({
simulation: true, simulation: true,
labelEditing: false, labelEditing: false,
@ -65,154 +67,27 @@ const controlForm = ref({
}) })
const model = ref<ModelApi.ModelVO>() // const model = ref<ModelApi.ModelVO>() //
// bpmnInstances
const initBpmnInstances = () => {
if (!modeler.value) return false
try {
const instances = {
modeler: modeler.value,
modeling: modeler.value.get('modeling'),
moddle: modeler.value.get('moddle'),
eventBus: modeler.value.get('eventBus'),
bpmnFactory: modeler.value.get('bpmnFactory'),
elementFactory: modeler.value.get('elementFactory'),
elementRegistry: modeler.value.get('elementRegistry'),
replace: modeler.value.get('replace'),
selection: modeler.value.get('selection')
}
//
return Object.values(instances).every((instance) => instance)
} catch (error) {
console.error('初始化 bpmnInstances 失败:', error)
return false
}
}
/** 初始化 modeler */ /** 初始化 modeler */
const initModeler = async (item) => { const initModeler = async (item) => {
try { modeler.value = item
modeler.value = item
// modeler
await nextTick()
// modeler
if (initBpmnInstances()) {
isModelerReady.value = true
emit('init-finished')
//
if (props.modelId) {
//
const data = await ModelApi.getModel(props.modelId)
model.value = {
...data,
bpmnXml: undefined // bpmnXml
}
xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
} else if (props.modelKey && props.modelName) {
//
xmlString.value = props.value || getDefaultBpmnXml(props.modelKey, props.modelName)
model.value = {
key: props.modelKey,
name: props.modelName
} as ModelApi.ModelVO
}
// XML
await nextTick()
try {
await modeler.value.importXML(xmlString.value)
if (processDesigner.value?.refresh) {
processDesigner.value.refresh()
}
} catch (error) {
console.error('导入XML失败:', error)
}
} else {
console.error('modeler 实例未完全初始化')
}
} catch (error) {
console.error('初始化 modeler 失败:', error)
}
} }
/** 获取默认的BPMN XML */
const getDefaultBpmnXml = (key: string, name: string) => {
return `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
<process id="${key}" name="${name}" isExecutable="true" />
<bpmndi:BPMNDiagram id="BPMNDiagram">
<bpmndi:BPMNPlane id="${key}_di" bpmnElement="${key}" />
</bpmndi:BPMNDiagram>
</definitions>`
}
/** 添加/修改模型 */ /** 添加/修改模型 */
const save = async (bpmnXml: string) => { const save = async (bpmnXml: string) => {
try { try {
xmlString.value = bpmnXml xmlString.value = bpmnXml
if (props.modelId) { emit('success', bpmnXml)
//
const data = {
...model.value,
bpmnXml: bpmnXml
} as unknown as ModelApi.ModelVO
await ModelApi.updateModelBpmn(data)
emit('success')
} else {
// XML
emit('success', bpmnXml)
}
} catch (error) { } catch (error) {
console.error('保存失败:', error) console.error('保存失败:', error)
message.error('保存失败') message.error('保存失败')
} }
} }
// keyname value
watch(
[() => props.modelKey, () => props.modelName, () => props.value],
async ([newKey, newName, newValue]) => {
if (!props.modelId && isModelerReady.value) {
let shouldRefresh = false
if (newKey && newName) {
const newXml = newValue || getDefaultBpmnXml(newKey, newName)
if (newXml !== xmlString.value) {
xmlString.value = newXml
shouldRefresh = true
}
model.value = {
...model.value,
key: newKey,
name: newName
} as ModelApi.ModelVO
} else if (newValue && newValue !== xmlString.value) {
xmlString.value = newValue
shouldRefresh = true
}
if (shouldRefresh) {
//
await nextTick()
if (processDesigner.value?.refresh) {
try {
await modeler.value?.importXML(xmlString.value)
processDesigner.value.refresh()
} catch (error) {
console.error('导入XML失败:', error)
}
}
}
}
},
{ deep: true }
)
// //
onBeforeUnmount(() => { onBeforeUnmount(() => {
isModelerReady.value = false
modeler.value = null modeler.value = null
// //
const w = window as any const w = window as any
@ -221,54 +96,7 @@ onBeforeUnmount(() => {
} }
}) })
/** 获取 XML 字符串 */
const saveXML = async () => {
if (!modeler.value) {
return { xml: xmlString.value }
}
try {
const result = await modeler.value.saveXML({ format: true })
xmlString.value = result.xml
return result
} catch (error) {
console.error('获取XML失败:', error)
return { xml: xmlString.value }
}
}
/** 获取SVG字符串 */
const saveSVG = async () => {
if (!modeler.value) {
return { svg: undefined }
}
try {
return await modeler.value.saveSVG()
} catch (error) {
console.error('获取SVG失败:', error)
return { svg: undefined }
}
}
/** 刷新视图 */
const refresh = async () => {
if (processDesigner.value?.refresh && modeler.value) {
try {
await modeler.value.importXML(xmlString.value)
processDesigner.value.refresh()
} catch (error) {
console.error('刷新视图失败:', error)
}
}
}
//
defineExpose({
modeler,
isModelerReady,
saveXML,
saveSVG,
refresh
})
</script> </script>
<style lang="scss"> <style lang="scss">
.process-panel__container { .process-panel__container {

View File

@ -144,10 +144,6 @@ import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
import { UserVO } from '@/api/system/user' import { UserVO } from '@/api/system/user'
const props = defineProps({ const props = defineProps({
modelValue: {
type: Object,
required: true
},
categoryList: { categoryList: {
type: Array, type: Array,
required: true required: true
@ -158,8 +154,6 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['update:modelValue'])
const formRef = ref() const formRef = ref()
const selectedStartUsers = ref<UserVO[]>([]) const selectedStartUsers = ref<UserVO[]>([])
const selectedManagerUsers = ref<UserVO[]>([]) const selectedManagerUsers = ref<UserVO[]>([])
@ -177,27 +171,30 @@ const rules = {
} }
// //
const modelData = computed({ const modelData = defineModel<any>()
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// //
watch( watch(
() => props.modelValue, () => modelData.value,
(newVal) => { (newVal) => {
if (newVal.startUserIds?.length) { if (newVal.startUserIds?.length) {
selectedStartUsers.value = props.userList.filter((user: UserVO) => selectedStartUsers.value = props.userList.filter((user: UserVO) =>
newVal.startUserIds.includes(user.id) newVal.startUserIds.includes(user.id)
) as UserVO[] ) as UserVO[]
} else {
selectedStartUsers.value = []
} }
if (newVal.managerUserIds?.length) { if (newVal.managerUserIds?.length) {
selectedManagerUsers.value = props.userList.filter((user: UserVO) => selectedManagerUsers.value = props.userList.filter((user: UserVO) =>
newVal.managerUserIds.includes(user.id) newVal.managerUserIds.includes(user.id)
) as UserVO[] ) as UserVO[]
} else {
selectedManagerUsers.value = []
} }
}, },
{ immediate: true } {
immediate: true
}
) )
/** 打开发起人选择 */ /** 打开发起人选择 */
@ -215,58 +212,52 @@ const openManagerUserSelect = () => {
/** 处理用户选择确认 */ /** 处理用户选择确认 */
const handleUserSelectConfirm = (_, users: UserVO[]) => { const handleUserSelectConfirm = (_, users: UserVO[]) => {
if (currentSelectType.value === 'start') { if (currentSelectType.value === 'start') {
selectedStartUsers.value = users modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
startUserIds: users.map((u) => u.id) startUserIds: users.map((u) => u.id)
}) }
} else { } else {
selectedManagerUsers.value = users modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
managerUserIds: users.map((u) => u.id) managerUserIds: users.map((u) => u.id)
}) }
} }
} }
/** 处理发起人类型变化 */ /** 处理发起人类型变化 */
const handleStartUserTypeChange = (value: number) => { const handleStartUserTypeChange = (value: number) => {
if (value !== 1) { if (value !== 1) {
selectedStartUsers.value = [] modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
startUserIds: [] startUserIds: []
}) }
} }
} }
/** 处理管理员类型变化 */ /** 处理管理员类型变化 */
const handleManagerUserTypeChange = (value: number) => { const handleManagerUserTypeChange = (value: number) => {
if (value !== 1) { if (value !== 1) {
selectedManagerUsers.value = [] modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
managerUserIds: [] managerUserIds: []
}) }
} }
} }
/** 移除发起人 */ /** 移除发起人 */
const handleRemoveStartUser = (user: UserVO) => { const handleRemoveStartUser = (user: UserVO) => {
selectedStartUsers.value = selectedStartUsers.value.filter((u) => u.id !== user.id) modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
startUserIds: modelData.value.startUserIds.filter((id: number) => id !== user.id) startUserIds: modelData.value.startUserIds.filter((id: number) => id !== user.id)
}) }
} }
/** 移除管理员 */ /** 移除管理员 */
const handleRemoveManagerUser = (user: UserVO) => { const handleRemoveManagerUser = (user: UserVO) => {
selectedManagerUsers.value = selectedManagerUsers.value.filter((u) => u.id !== user.id) modelData.value = {
emit('update:modelValue', {
...modelData.value, ...modelData.value,
managerUserIds: modelData.value.managerUserIds.filter((id: number) => id !== user.id) managerUserIds: modelData.value.managerUserIds.filter((id: number) => id !== user.id)
}) }
} }
/** 表单校验 */ /** 表单校验 */

View File

@ -70,25 +70,16 @@ import * as FormApi from '@/api/bpm/form'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
const props = defineProps({ const props = defineProps({
modelValue: {
type: Object,
required: true
},
formList: { formList: {
type: Array, type: Array,
required: true required: true
} }
}) })
const emit = defineEmits(['update:modelValue'])
const formRef = ref() const formRef = ref()
// //
const modelData = computed({ const modelData = defineModel<any>()
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// //
const formPreview = ref({ const formPreview = ref({

View File

@ -6,10 +6,7 @@
:model-id="modelData.id" :model-id="modelData.id"
:model-key="modelData.key" :model-key="modelData.key"
:model-name="modelData.name" :model-name="modelData.name"
:value="currentBpmnXml"
ref="bpmnEditorRef"
@success="handleDesignSuccess" @success="handleDesignSuccess"
@init-finished="handleEditorInit"
/> />
</template> </template>
@ -21,10 +18,7 @@
:model-key="modelData.key" :model-key="modelData.key"
:model-name="modelData.name" :model-name="modelData.name"
:start-user-ids="modelData.startUserIds" :start-user-ids="modelData.startUserIds"
:value="currentSimpleModel"
ref="simpleEditorRef"
@success="handleDesignSuccess" @success="handleDesignSuccess"
@init-finished="handleEditorInit"
/> />
</template> </template>
</template> </template>
@ -34,137 +28,16 @@ import { BpmModelType } from '@/utils/constants'
import BpmModelEditor from '../editor/index.vue' import BpmModelEditor from '../editor/index.vue'
import SimpleModelDesign from '../../simple/SimpleModelDesign.vue' import SimpleModelDesign from '../../simple/SimpleModelDesign.vue'
const props = defineProps({
modelValue: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:modelValue', 'success'])
const bpmnEditorRef = ref()
const simpleEditorRef = ref()
const isEditorInitialized = ref(false)
// //
const modelData = computed({ const modelData = defineModel<any>()
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
// XML const processData = inject('processData') as Ref
const currentBpmnXml = ref('')
const currentSimpleModel = ref('')
// XML
const initOrUpdateXmlData = () => {
if (modelData.value) {
if (modelData.value.type === BpmModelType.BPMN) {
currentBpmnXml.value = modelData.value.bpmnXml || ''
} else {
currentSimpleModel.value = modelData.value.simpleModel || ''
}
}
}
// modelValue
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
if (newVal.type === BpmModelType.BPMN) {
if (newVal.bpmnXml && newVal.bpmnXml !== currentBpmnXml.value) {
currentBpmnXml.value = newVal.bpmnXml
//
if (isEditorInitialized.value && bpmnEditorRef.value?.refresh) {
nextTick(() => {
bpmnEditorRef.value.refresh()
})
}
}
} else {
if (newVal.simpleModel && newVal.simpleModel !== currentSimpleModel.value) {
currentSimpleModel.value = newVal.simpleModel
//
if (isEditorInitialized.value && simpleEditorRef.value?.refresh) {
nextTick(() => {
simpleEditorRef.value.refresh()
})
}
}
}
}
},
{ immediate: true, deep: true }
)
/** 编辑器初始化完成的回调 */
const handleEditorInit = async () => {
isEditorInitialized.value = true
// tick
await nextTick()
//
if (modelData.value.type === BpmModelType.BPMN) {
if (modelData.value.bpmnXml) {
currentBpmnXml.value = modelData.value.bpmnXml
if (bpmnEditorRef.value?.refresh) {
await nextTick()
bpmnEditorRef.value.refresh()
}
}
} else {
if (modelData.value.simpleModel) {
currentSimpleModel.value = modelData.value.simpleModel
if (simpleEditorRef.value?.refresh) {
await nextTick()
simpleEditorRef.value.refresh()
}
}
}
}
/** 获取当前流程数据 */
const getProcessData = async () => {
try {
if (modelData.value.type === BpmModelType.BPMN) {
if (!bpmnEditorRef.value || !isEditorInitialized.value) {
return currentBpmnXml.value || undefined
}
const { xml } = await bpmnEditorRef.value.saveXML()
if (xml) {
currentBpmnXml.value = xml
return xml
}
} else {
if (!simpleEditorRef.value || !isEditorInitialized.value) {
return currentSimpleModel.value || undefined
}
const flowData = await simpleEditorRef.value.getCurrentFlowData()
if (flowData) {
currentSimpleModel.value = flowData
return flowData
}
}
return modelData.value.type === BpmModelType.BPMN
? currentBpmnXml.value
: currentSimpleModel.value
} catch (error) {
console.error('获取流程数据失败:', error)
return modelData.value.type === BpmModelType.BPMN
? currentBpmnXml.value
: currentSimpleModel.value
}
}
/** 表单校验 */ /** 表单校验 */
const validate = async () => { const validate = async () => {
try { try {
// //
const processData = await getProcessData() if (!processData.value) {
if (!processData) {
throw new Error('请设计流程') throw new Error('请设计流程')
} }
return true return true
@ -172,27 +45,19 @@ const validate = async () => {
throw error throw error
} }
} }
/** 处理设计器保存成功 */ /** 处理设计器保存成功 */
const handleDesignSuccess = async (data?: any) => { const handleDesignSuccess = async (data?: any) => {
if (data) { if (data) {
if (modelData.value.type === BpmModelType.BPMN) {
currentBpmnXml.value = data
} else {
currentSimpleModel.value = data
}
// //
const newModelData = { const newModelData = {
...modelData.value, ...modelData.value,
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null, bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
} }
// 使emit // 使emit
await nextTick() await nextTick()
emit('update:modelValue', newModelData) //
emit('success', data) modelData.value = newModelData
} }
} }
@ -203,33 +68,14 @@ const showDesigner = computed(() => {
// //
onMounted(() => { onMounted(() => {
initOrUpdateXmlData()
}) })
// //
onBeforeUnmount(async () => { onBeforeUnmount(async () => {
try {
//
const data = await getProcessData()
if (data) {
//
const newModelData = {
...modelData.value,
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
}
// 使emit
await nextTick()
emit('update:modelValue', newModelData)
}
} catch (error) {
console.error('保存数据失败:', error)
}
}) })
defineExpose({ defineExpose({
validate, validate
getProcessData
}) })
</script> </script>

View File

@ -71,7 +71,6 @@
v-if="currentStep === 2" v-if="currentStep === 2"
v-model="formData" v-model="formData"
ref="processDesignRef" ref="processDesignRef"
@success="handleDesignSuccess"
/> />
</div> </div>
</div> </div>
@ -118,7 +117,8 @@ const validateProcess = async () => {
await processDesignRef.value?.validate() await processDesignRef.value?.validate()
} }
const currentStep = ref(0) // const currentStep = ref(-1) //
const steps = [ const steps = [
{ title: '基本信息', validator: validateBasic }, { title: '基本信息', validator: validateBasic },
{ title: '表单设计', validator: validateForm }, { title: '表单设计', validator: validateForm },
@ -145,17 +145,24 @@ const formData: any = ref({
managerUserIds: [] managerUserIds: []
}) })
//
const processData = ref<any>()
provide("processData", processData)
// //
const formList = ref([]) const formList = ref([])
const categoryList = ref([]) const categoryList = ref([])
const userList = ref<UserApi.UserVO[]>([]) const userList = ref<UserApi.UserVO[]>([])
/** 初始化数据 */ /** 初始化数据 */
const initData = async () => { const initData = async () => {
const modelId = route.params.id as string const modelId = route.params.id as string
if (modelId) { if (modelId) {
// //
formData.value = await ModelApi.getModel(modelId) formData.value = await ModelApi.getModel(modelId)
} else { } else {
// //
formData.value.managerUserIds.push(userStore.getUser.id) formData.value.managerUserIds.push(userStore.getUser.id)
@ -167,59 +174,49 @@ const initData = async () => {
categoryList.value = await CategoryApi.getCategorySimpleList() categoryList.value = await CategoryApi.getCategorySimpleList()
// //
userList.value = await UserApi.getSimpleUserList() userList.value = await UserApi.getSimpleUserList()
currentStep.value = 0
} }
//
watch(async () => formData.value.type, (newValue, oldValue) => {
if (formData.value.type === BpmModelType.BPMN) {
processData.value = formData.value.bpmnXml
} else if (formData.value.type === BpmModelType.SIMPLE) {
processData.value = formData.value.simpleModel
}
console.log('加载流程数据', processData.value)
}, {
immediate: true,
})
/** 校验所有步骤数据是否完整 */ /** 校验所有步骤数据是否完整 */
const validateAllSteps = async () => { const validateAllSteps = async () => {
try { try {
// //
await basicInfoRef.value?.validate() try {
if (!formData.value.key || !formData.value.name || !formData.value.category) { await validateBasic()
} catch (error) {
currentStep.value = 0 currentStep.value = 0
throw new Error('请完善基本信息') throw new Error('请完善基本信息')
} }
// //
await formDesignRef.value?.validate() try {
if (formData.value.formType === 10 && !formData.value.formId) { await validateForm()
currentStep.value = 1 } catch (error) {
throw new Error('请选择流程表单')
}
if (
formData.value.formType === 20 &&
(!formData.value.formCustomCreatePath || !formData.value.formCustomViewPath)
) {
currentStep.value = 1 currentStep.value = 1
throw new Error('请完善自定义表单信息') throw new Error('请完善自定义表单信息')
} }
// //
//
if (!formData.value.bpmnXml && !formData.value.simpleModel) {
//
if (currentStep.value !== 2) {
await steps[currentStep.value].validator()
//
currentStep.value = 2
//
await nextTick()
}
// //
await processDesignRef.value?.validate() try {
const processData = await processDesignRef.value?.getProcessData() await validateProcess()
if (!processData) { } catch (error) {
throw new Error('请设计流程') currentStep.value = 2
} throw new Error('请设计流程')
//
if (formData.value.type === BpmModelType.BPMN) {
formData.value.bpmnXml = processData
formData.value.simpleModel = null
} else {
formData.value.bpmnXml = null
formData.value.simpleModel = processData
}
} }
return true return true
@ -239,20 +236,6 @@ const handleSave = async () => {
...formData.value ...formData.value
} }
//
if (currentStep.value === 2) {
const processData = await processDesignRef.value?.getProcessData()
if (processData) {
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData
}
}
}
if (formData.value.id) { if (formData.value.id) {
// //
await ModelApi.updateModel(modelData) await ModelApi.updateModel(modelData)
@ -308,20 +291,6 @@ const handleDeploy = async () => {
...formData.value ...formData.value
} }
//
if (currentStep.value === 2) {
const processData = await processDesignRef.value?.getProcessData()
if (processData) {
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData
}
}
}
// //
if (formData.value.id) { if (formData.value.id) {
await ModelApi.updateModel(modelData) await ModelApi.updateModel(modelData)
@ -344,59 +313,26 @@ const handleDeploy = async () => {
/** 步骤切换处理 */ /** 步骤切换处理 */
const handleStepClick = async (index: number) => { const handleStepClick = async (index: number) => {
try { try {
// keyname console.log('index', index);
if (index === 2) { if (index !== 0) {
if (!formData.value.key || !formData.value.name) { await validateBasic()
message.warning('请先填写流程标识和流程名称')
return
}
} }
if (index !== 1) {
// await validateForm()
if (currentStep.value === 2) { }
const processData = await processDesignRef.value?.getProcessData() if (index !== 2) {
if (processData) { await validateProcess()
if (formData.value.type === BpmModelType.BPMN) {
formData.value.bpmnXml = processData
formData.value.simpleModel = null
} else {
formData.value.bpmnXml = null
formData.value.simpleModel = processData
}
}
} else {
//
if (index > currentStep.value) {
if (typeof steps[currentStep.value].validator === 'function') {
await steps[currentStep.value].validator()
}
}
} }
// //
currentStep.value = index currentStep.value = index
//
if (index === 2) {
await nextTick()
//
await new Promise(resolve => setTimeout(resolve, 200))
if (processDesignRef.value?.refresh) {
await processDesignRef.value.refresh()
}
}
} catch (error) { } catch (error) {
console.error('步骤切换失败:', error) console.error('步骤切换失败:', error)
message.warning('请先完善当前步骤必填信息') message.warning('请先完善当前步骤必填信息')
} }
} }
/** 处理设计器保存成功 */
const handleDesignSuccess = (bpmnXml?: string) => {
if (bpmnXml) {
formData.value.bpmnXml = bpmnXml
}
}
/** 返回列表页 */ /** 返回列表页 */
const handleBack = () => { const handleBack = () => {

View File

@ -4,9 +4,7 @@
:model-id="modelId" :model-id="modelId"
:model-key="modelKey" :model-key="modelKey"
:model-name="modelName" :model-name="modelName"
:value="currentValue"
@success="handleSuccess" @success="handleSuccess"
@init-finished="handleInit"
:start-user-ids="startUserIds" :start-user-ids="startUserIds"
ref="designerRef" ref="designerRef"
/> />
@ -23,133 +21,30 @@ const props = defineProps<{
modelId?: string modelId?: string
modelKey?: string modelKey?: string
modelName?: string modelName?: string
value?: string
startUserIds?: number[] startUserIds?: number[]
}>() }>()
const emit = defineEmits(['success', 'init-finished']) const emit = defineEmits(['success'])
const designerRef = ref() const designerRef = ref()
const isInitialized = ref(false)
const currentValue = ref('')
//
const initOrUpdateValue = async () => {
console.log('initOrUpdateValue', props.value)
if (props.value) {
currentValue.value = props.value
//
if (isInitialized.value && designerRef.value) {
try {
await designerRef.value.loadProcessData(props.value)
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
} catch (error) {
console.error('加载流程数据失败:', error)
}
}
}
}
//
watch(
[() => props.modelKey, () => props.modelName, () => props.value],
async ([newKey, newName, newValue], [oldKey, oldName, oldValue]) => {
if (designerRef.value && isInitialized.value) {
try {
if (newKey && newName && (newKey !== oldKey || newName !== oldName)) {
await designerRef.value.updateModel(newKey, newName)
}
if (newValue && newValue !== oldValue) {
currentValue.value = newValue
await designerRef.value.loadProcessData(newValue)
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
}
} catch (error) {
console.error('更新流程数据失败:', error)
}
}
},
{ deep: true, immediate: true }
)
//
const handleInit = async () => {
try {
isInitialized.value = true
emit('init-finished')
// tick
await nextTick()
//
if (props.modelKey && props.modelName) {
await designerRef.value.updateModel(props.modelKey, props.modelName)
}
if (props.value) {
currentValue.value = props.value
await designerRef.value.loadProcessData(props.value)
//
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
}
} catch (error) {
console.error('初始化流程数据失败:', error)
}
}
// //
const handleSuccess = (data?: any) => { const handleSuccess = (data?: any) => {
console.warn('handleSuccess', data) console.info('handleSuccess', data)
if (data && data !== currentValue.value) { if (data) {
currentValue.value = data
emit('success', data) emit('success', data)
} }
} }
/** 获取当前流程数据 */
const getCurrentFlowData = async () => {
try {
if (designerRef.value) {
const data = await designerRef.value.getCurrentFlowData()
if (data) {
currentValue.value = data
}
return data
}
return currentValue.value || undefined
} catch (error) {
console.error('获取流程数据失败:', error)
return currentValue.value || undefined
}
}
// //
onMounted(() => { onMounted(() => {
initOrUpdateValue()
}) })
// //
onBeforeUnmount(async () => { onBeforeUnmount(async () => {
try {
const data = await getCurrentFlowData()
if (data) {
emit('success', data)
}
} catch (error) {
console.error('保存数据失败:', error)
}
}) })
defineExpose({ defineExpose({
getCurrentFlowData,
refresh: () => designerRef.value?.refresh?.()
}) })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>