From 37964e740f8691a58f9a43f976fb9c047e034e28 Mon Sep 17 00:00:00 2001
From: Lesan <1960681385@qq.com>
Date: Thu, 16 Jan 2025 13:55:15 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E5=AE=A1=E6=89=B9=E7=AD=BE?=
=?UTF-8?q?=E5=90=8D=E4=BB=A3=E7=A0=81=E8=AF=84=E5=AE=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/bpm/processInstance/index.ts | 2 +-
src/utils/download.ts | 27 ++++++++++++++
.../detail/ProcessInstanceOperationButton.vue | 16 ++++-----
.../detail/ProcessInstanceTimeline.vue | 6 ++--
.../bpm/processInstance/detail/SignDialog.vue | 35 ++-----------------
5 files changed, 42 insertions(+), 44 deletions(-)
diff --git a/src/api/bpm/processInstance/index.ts b/src/api/bpm/processInstance/index.ts
index a0f263de..9a99a91e 100644
--- a/src/api/bpm/processInstance/index.ts
+++ b/src/api/bpm/processInstance/index.ts
@@ -36,7 +36,7 @@ export type ApprovalTaskInfo = {
assigneeUser: User
status: number
reason: string
- sign: string // TODO @lesan:字段改成 signPicUrl 签名照片。只有 sign 感觉是签名文本哈。
+ signPicUrl: string
}
// 审批节点信息
diff --git a/src/utils/download.ts b/src/utils/download.ts
index 5bbfb9fe..bd58845f 100644
--- a/src/utils/download.ts
+++ b/src/utils/download.ts
@@ -65,6 +65,33 @@ const download = {
a.download = 'image.png'
a.click()
}
+ },
+ base64ToFile: (base64, fileName) => {
+ // 将base64按照 , 进行分割 将前缀 与后续内容分隔开
+ const data = base64.split(',')
+ // 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
+ const type = data[0].match(/:(.*?);/)[1]
+ // 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
+ const suffix = type.split('/')[1]
+ // 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
+ const bstr = window.atob(data[1])
+ // 获取解码结果字符串的长度
+ let n = bstr.length
+ // 根据解码结果字符串的长度创建一个等长的整形数字数组
+ // 但在创建时 所有元素初始值都为 0
+ const u8arr = new Uint8Array(n)
+ // 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
+ while (n--) {
+ // charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
+ u8arr[n] = bstr.charCodeAt(n)
+ }
+ // 利用构造函数创建File文件对象
+ // new File(bits, name, options)
+ const file = new File([u8arr], `${fileName}.${suffix}`, {
+ type: type
+ })
+ // 将File文件对象返回给方法的调用者
+ return file
}
}
diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue
index a4ecee65..402644e9 100644
--- a/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue
+++ b/src/views/bpm/processInstance/detail/ProcessInstanceOperationButton.vue
@@ -47,15 +47,15 @@
点击签名
@@ -553,11 +553,11 @@ const signRef = ref()
const approveSignFormRef = ref()
const approveReasonForm = reactive({
reason: '',
- sign: ''
+ signPicUrl: ''
})
const approveReasonRule = reactive>({
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
- sign: [{ required: true, message: '签名不能为空', trigger: 'change' }]
+ signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }]
})
// 拒绝表单
const rejectFormRef = ref()
@@ -705,7 +705,7 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
}
// 签名
if (runningTask.value.signEnable) {
- data.sign = approveReasonForm.sign
+ data.signPicUrl = approveReasonForm.signPicUrl
}
// 多表单处理,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
// TODO 芋艿 任务有多表单这里要如何处理,会和可编辑的字段冲突
@@ -1002,7 +1002,7 @@ const getUpdatedProcessInstanceVariables = () => {
/** 处理签名完成 */
const handleSignFinish = (url: string) => {
- approveReasonForm.sign = url
+ approveReasonForm.signPicUrl = url
approveSignFormRef.value.validate('change')
}
diff --git a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
index 1fd31dcf..fcd5ec89 100644
--- a/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
+++ b/src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue
@@ -124,14 +124,14 @@
审批意见:{{ task.reason }}
签名:
diff --git a/src/views/bpm/processInstance/detail/SignDialog.vue b/src/views/bpm/processInstance/detail/SignDialog.vue
index 211cfcda..744a3556 100644
--- a/src/views/bpm/processInstance/detail/SignDialog.vue
+++ b/src/views/bpm/processInstance/detail/SignDialog.vue
@@ -2,9 +2,8 @@
-
import Vue3Signature from 'vue3-signature'
import * as FileApi from '@/api/infra/file'
+import download from '@/utils/download'
const message = useMessage() // 消息弹窗
const signDialogVisible = ref(false)
@@ -40,40 +40,11 @@ const emits = defineEmits(['success'])
const submit = async () => {
message.success('签名上传中请稍等。。。')
const res = await FileApi.updateFile({
- file: base64ToFile(signature.value.save('image/png'), '签名')
+ file: download.base64ToFile(signature.value.save('image/png'), '签名')
})
emits('success', res.data)
signDialogVisible.value = false
}
-
-// TODO @lesan:这个要不抽到 download.js 里,让这个组件更简洁干净?
-const base64ToFile = (base64, fileName) => {
- // 将base64按照 , 进行分割 将前缀 与后续内容分隔开
- let data = base64.split(',')
- // 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
- let type = data[0].match(/:(.*?);/)[1]
- // 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
- let suffix = type.split('/')[1]
- // 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
- const bstr = window.atob(data[1])
- // 获取解码结果字符串的长度
- let n = bstr.length
- // 根据解码结果字符串的长度创建一个等长的整形数字数组
- // 但在创建时 所有元素初始值都为 0
- const u8arr = new Uint8Array(n)
- // 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
- while (n--) {
- // charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
- u8arr[n] = bstr.charCodeAt(n)
- }
- // 利用构造函数创建File文件对象
- // new File(bits, name, options)
- const file = new File([u8arr], `${fileName}.${suffix}`, {
- type: type
- })
- // 将File文件对象返回给方法的调用者
- return file
-}
--
Gitee
From 69ccd83af31c33a92def15100706fe336419d6c0 Mon Sep 17 00:00:00 2001
From: Lesan <1960681385@qq.com>
Date: Thu, 16 Jan 2025 14:40:14 +0800
Subject: [PATCH 2/4] =?UTF-8?q?feat:=20bpmn=E8=AE=BE=E8=AE=A1=E5=99=A8?=
=?UTF-8?q?=E6=B7=BB=E5=8A=A0UserTask=E7=AD=BE=E5=90=8D=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../plugins/descriptor/flowableDescriptor.json | 14 ++++++++++++++
.../package/penal/PropertiesPanel.vue | 2 +-
.../components/UserTaskCustomConfig.vue | 17 ++++++++++++++++-
3 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json
index 7fe1fa79..e50d3ce3 100644
--- a/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json
+++ b/src/components/bpmnProcessDesigner/package/designer/plugins/descriptor/flowableDescriptor.json
@@ -1438,6 +1438,20 @@
"isBody": true
}
]
+ },
+ {
+ "name": "SignEnable",
+ "superClass": ["Element"],
+ "meta": {
+ "allowedIn": ["bpmn:UserTask"]
+ },
+ "properties": [
+ {
+ "name": "value",
+ "type": "Boolean",
+ "isBody": true
+ }
+ ]
}
],
"emumerations": []
diff --git a/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue b/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
index e53ad994..ff08dd33 100644
--- a/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
+++ b/src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
@@ -1,5 +1,5 @@
-
+
diff --git a/src/components/bpmnProcessDesigner/package/penal/custom-config/components/UserTaskCustomConfig.vue b/src/components/bpmnProcessDesigner/package/penal/custom-config/components/UserTaskCustomConfig.vue
index ba385145..206c3889 100644
--- a/src/components/bpmnProcessDesigner/package/penal/custom-config/components/UserTaskCustomConfig.vue
+++ b/src/components/bpmnProcessDesigner/package/penal/custom-config/components/UserTaskCustomConfig.vue
@@ -5,6 +5,7 @@
4. 操作按钮
5. 字段权限
6. 审批类型
+ 7. 是否需要签名
-->
@@ -161,6 +162,11 @@
+
+
是否需要签名
+
+
+
@@ -218,6 +224,9 @@ const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFie
// 审批类型
const approveType = ref({ value: ApproveType.USER })
+// 是否需要签名
+const signEnable = ref({ value: false })
+
const elExtensionElements = ref()
const otherExtensions = ref()
const bpmnElement = ref()
@@ -325,6 +334,11 @@ const resetCustomConfigList = () => {
ex.$type !== `${prefix}:ApproveType`
) ?? []
+ // 是否需要签名
+ signEnable.value =
+ elExtensionElements.value.values?.filter((ex) => ex.$type === `${prefix}:SignEnable`)?.[0] ||
+ bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false })
+
// 更新元素扩展属性,避免后续报错
updateElementExtensions()
}
@@ -373,7 +387,8 @@ const updateElementExtensions = () => {
assignEmptyUserIdsEl.value,
approveType.value,
...buttonsSettingEl.value,
- ...fieldsPermissionEl.value
+ ...fieldsPermissionEl.value,
+ signEnable.value
]
})
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
--
Gitee
From 38578c3e50a707de073b6ff72ddfc62042717158 Mon Sep 17 00:00:00 2001
From: Lesan <1960681385@qq.com>
Date: Thu, 16 Jan 2025 14:55:32 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E8=8E=B7=E5=8F=96=E5=88=86?=
=?UTF-8?q?=E6=94=AF=E4=B8=AD=E7=9A=84=E8=8A=82=E7=82=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/nodes-config/RouterNodeConfig.vue | 20 +++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/components/SimpleProcessDesignerV2/src/nodes-config/RouterNodeConfig.vue b/src/components/SimpleProcessDesignerV2/src/nodes-config/RouterNodeConfig.vue
index 6748a736..a7c4ac9b 100644
--- a/src/components/SimpleProcessDesignerV2/src/nodes-config/RouterNodeConfig.vue
+++ b/src/components/SimpleProcessDesignerV2/src/nodes-config/RouterNodeConfig.vue
@@ -86,7 +86,7 @@ const currentNode = useWatchNode(props)
// 节点名称
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
const routerGroups = ref([])
-const nodeOptions = ref()
+const nodeOptions = ref([])
const conditionRef = ref([])
/** 保存配置 */
@@ -94,7 +94,7 @@ const saveConfig = async () => {
// 校验表单
let valid = true
for (const item of conditionRef.value) {
- if (!(await item.validate())) {
+ if (item && !(await item.validate())) {
valid = false
}
}
@@ -109,7 +109,7 @@ const saveConfig = async () => {
}
// 显示路由分支节点配置, 由父组件传过来
const showRouteNodeConfig = (node: SimpleFlowNode) => {
- getRouterNode()
+ getRouterNode(processNodeTree?.value)
routerGroups.value = []
nodeName.value = node.name
if (node.routerGroups) {
@@ -172,15 +172,14 @@ const deleteRouterGroup = (index: number) => {
routerGroups.value.splice(index, 1)
}
-const getRouterNode = () => {
- // TODO @lesan 还需要满足以下要求
+// 递归获取所有节点
+const getRouterNode = (node) => {
+ // TODO 最好还需要满足以下要求
// 并行分支、包容分支内部节点不能跳转到外部节点
// 条件分支节点可以向上跳转到外部节点
- let node = processNodeTree?.value
- nodeOptions.value = []
while (true) {
if (!node) break
- if (node.type !== NodeType.ROUTER_BRANCH_NODE) {
+ if (node.type !== NodeType.ROUTER_BRANCH_NODE && node.type !== NodeType.CONDITION_NODE) {
nodeOptions.value.push({
label: node.name,
value: node.id
@@ -189,6 +188,11 @@ const getRouterNode = () => {
if (!node.childNode || node.type === NodeType.END_EVENT_NODE) {
break
}
+ if (node.conditionNodes && node.conditionNodes.length) {
+ node.conditionNodes.forEach((item) => {
+ getRouterNode(item)
+ })
+ }
node = node.childNode
}
}
--
Gitee
From 9308eceb81d79bf924eea5fb6975d2fd792a22b1 Mon Sep 17 00:00:00 2001
From: Lesan <1960681385@qq.com>
Date: Thu, 16 Jan 2025 15:25:20 +0800
Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E6=8A=BD=E7=A6=BBUserTaskListener?=
=?UTF-8?q?=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/nodes-config/UserTaskNodeConfig.vue | 248 +----------------
.../components/UserTaskListener.vue | 261 ++++++++++++++++++
2 files changed, 267 insertions(+), 242 deletions(-)
create mode 100644 src/components/SimpleProcessDesignerV2/src/nodes-config/components/UserTaskListener.vue
diff --git a/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
index 64acc2c3..8e18b756 100644
--- a/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
+++ b/src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue
@@ -440,217 +440,8 @@
-
-
-
-
- {{ listener.name }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 添加一行
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 添加一行
-
-
-
-
-
+
@@ -687,9 +478,7 @@ import {
ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType,
FieldPermissionType,
- ProcessVariableEnum,
- LISTENER_MAP_TYPES,
- ListenerParamTypeEnum
+ ProcessVariableEnum
} from '../consts'
import {
@@ -703,6 +492,7 @@ import {
import { defaultProps } from '@/utils/tree'
import { cloneDeep } from 'lodash-es'
import { convertTimeUnit, getApproveTypeText } from '../utils'
+import UserTaskListener from './components/UserTaskListener.vue'
defineOptions({
name: 'UserTaskNodeConfig'
})
@@ -780,21 +570,6 @@ const formRules = reactive({
assignEmptyHandlerUserIds: [{ required: true, message: '用户不能为空', trigger: 'change' }],
assignStartUserHandlerType: [{ required: true }]
})
-// 监听器数组
-const taskListener = ref([
- {
- name: '创建任务',
- type: 'Create'
- },
- {
- name: '指派任务执行人员',
- type: 'Assign'
- },
- {
- name: '完成任务',
- type: 'Complete'
- }
-])
const {
configForm: tempConfigForm,
@@ -843,7 +618,7 @@ const {
cTimeoutMaxRemindCount
} = useTimeoutHandler()
-const listenerFormRef = ref()
+const userTaskListenerRef = ref()
// 保存配置
const saveConfig = async () => {
@@ -860,8 +635,8 @@ const saveConfig = async () => {
}
if (!formRef) return false
- if (!listenerFormRef) return false
- const valid = (await formRef.value.validate()) && (await listenerFormRef.value.validate())
+ if (!userTaskListenerRef) return false
+ const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
if (!valid) return false
const showText = getShowText()
if (!showText) return false
@@ -1104,17 +879,6 @@ function useTimeoutHandler() {
cTimeoutMaxRemindCount
}
}
-
-const addTaskListenerParam = (arr) => {
- arr.push({
- key: '',
- type: 1,
- value: ''
- })
-}
-const deleteTaskListenerParam = (arr, index) => {
- arr.splice(index, 1)
-}