Appearance
@gauss/flowable
工作流扩展包,内置基于 BPMN 2.0 封装的流程引擎,支持流程定义、流程实例、任务等的管理。
包含 7 个页面,分别是
管理员角色: 角色管理,分类管理,流程模板,流程监控
普通角色: 我的流程,待办任务,已办任务
安装
shell
$ yarn add @gauss/flowable -S$ yarn add @gauss/flowable -S使用
导入路由
src/routes/index.js
js
import { flowableRoutes } from '@gauss/flowable'
import { createRoutes } from '@gauss/prime'
export const routes = createRoutes([
...flowableRoutes,
// ...其他路由
])import { flowableRoutes } from '@gauss/flowable'
import { createRoutes } from '@gauss/prime'
export const routes = createRoutes([
...flowableRoutes,
// ...其他路由
])注册表单组件

src/main.js
js
import { locales, provide } from '@gauss/flowable'
import { merge } from 'lodash-es'
import TaskForm from './components/task-form/index.vue'
import en from './locales/en'
import zh from './locales/zh'
provide({ TaskForm })
const prime = createPrime({
i18nOptions: {
// ...其它配置
// 国际化
messages: {
zh: merge(locales.zh, zh),
en: merge(locales.en, en),
},
},
})import { locales, provide } from '@gauss/flowable'
import { merge } from 'lodash-es'
import TaskForm from './components/task-form/index.vue'
import en from './locales/en'
import zh from './locales/zh'
provide({ TaskForm })
const prime = createPrime({
i18nOptions: {
// ...其它配置
// 国际化
messages: {
zh: merge(locales.zh, zh),
en: merge(locales.en, en),
},
},
})表单组件
表单组件需暴露 getFormData,getFormData 是 promise
src/components/task-form/index.vue
vue
<template>
<component
:is="getComponent(template.key)"
:type="type"
:template="template"
:taskNodes="taskNodes"
:getApplyInfo="getApplyInfo"
ref="taskFormRef"
></component>
</template>
<script setup>
import { taskFormProps } from '@gauss/flowable'
import { defineProps, ref } from 'vue'
import { getComponent } from './form'
defineProps(taskFormProps)
const taskFormRef = ref()
defineExpose({
getFormData() {
return taskFormRef.value.getFormData()
},
})
</script><template>
<component
:is="getComponent(template.key)"
:type="type"
:template="template"
:taskNodes="taskNodes"
:getApplyInfo="getApplyInfo"
ref="taskFormRef"
></component>
</template>
<script setup>
import { taskFormProps } from '@gauss/flowable'
import { defineProps, ref } from 'vue'
import { getComponent } from './form'
defineProps(taskFormProps)
const taskFormRef = ref()
defineExpose({
getFormData() {
return taskFormRef.value.getFormData()
},
})
</script>src/components/task-form/form/index.js
js
import AdjustmentForm from './adjustment-form.vue'
// 不同的流程注册不同的表单
const forms = {
process_IBUedushenqing_diaozhengdan: AdjustmentForm,
}
export const getComponent = key => {
return forms[key] || AdjustmentForm
}import AdjustmentForm from './adjustment-form.vue'
// 不同的流程注册不同的表单
const forms = {
process_IBUedushenqing_diaozhengdan: AdjustmentForm,
}
export const getComponent = key => {
return forms[key] || AdjustmentForm
}src/components/task-form/form/adjustment-form.vue
vue
<template>
<h-form-render :config="formConfig" ref="formRef"></h-form-render>
</template>
<script setup>
import { FLOW_TASK_TYPE_CREATE, FLOW_TASK_TYPE_VIEW, taskFormProps } from '@gauss/flowable'
import { useUserInfo } from '@gauss/prime'
import { defineFRConfig } from '@hamlet/render'
import { defineExpose, defineProps, nextTick, ref, watch } from 'vue'
const props = defineProps(taskFormProps)
const formRef = ref()
const userInfo = useUserInfo()
watch(
() => props.type,
value => {
// 如果不是创建状态,获取流程初始提交信息
if (value !== FLOW_TASK_TYPE_CREATE) {
props.getApplyInfo().then(res => {
const { name, department, companyCode, applyCreditLimit, applyPaymentDays } =
res?.applyInfo || {}
formRef.value.setFieldsValue({
name,
department,
companyCode,
applyCreditLimit,
applyPaymentDays,
})
})
} else {
// 否则获取当前用户信息并赋值
nextTick(() => {
formRef.value.setFieldsValue({
name: userInfo.userName,
department: userInfo.departmentName,
})
})
}
},
{
immediate: true,
},
)
const formConfig = defineFRConfig({
type: 'form',
labelPosition: 'top',
rowGutter: 16,
labelWidth: 0,
column: 2,
size: 'middle',
body: [
{
prop: 'name',
label: '申请人',
type: 'input',
disabled: true,
},
{
prop: 'department',
label: '申请人部门',
type: 'input',
disabled: true,
},
{
prop: 'companyCode',
label: '所属公司',
type: 'select',
required: true,
map: {
D802: '总部-D802',
F800: '总部-F800',
O801: '总部-O801',
O802: '子公司-O802',
O803: '子公司-O803',
O804: '子公司-O804',
O805: '子公司-O805',
},
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'applyCreditLimit',
label: '信贷额度',
type: 'input',
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'applyPaymentDays',
label: '付款日',
type: 'input',
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'comment',
label: '签字意见',
type: 'textarea',
rows: 2,
required: true,
colSpan: 2,
visible() {
return props.type !== FLOW_TASK_TYPE_VIEW
},
},
],
})
function getFormData() {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(formData => {
resolve(formData)
})
.catch(() => {
reject()
})
})
}
defineExpose({ getFormData })
</script><template>
<h-form-render :config="formConfig" ref="formRef"></h-form-render>
</template>
<script setup>
import { FLOW_TASK_TYPE_CREATE, FLOW_TASK_TYPE_VIEW, taskFormProps } from '@gauss/flowable'
import { useUserInfo } from '@gauss/prime'
import { defineFRConfig } from '@hamlet/render'
import { defineExpose, defineProps, nextTick, ref, watch } from 'vue'
const props = defineProps(taskFormProps)
const formRef = ref()
const userInfo = useUserInfo()
watch(
() => props.type,
value => {
// 如果不是创建状态,获取流程初始提交信息
if (value !== FLOW_TASK_TYPE_CREATE) {
props.getApplyInfo().then(res => {
const { name, department, companyCode, applyCreditLimit, applyPaymentDays } =
res?.applyInfo || {}
formRef.value.setFieldsValue({
name,
department,
companyCode,
applyCreditLimit,
applyPaymentDays,
})
})
} else {
// 否则获取当前用户信息并赋值
nextTick(() => {
formRef.value.setFieldsValue({
name: userInfo.userName,
department: userInfo.departmentName,
})
})
}
},
{
immediate: true,
},
)
const formConfig = defineFRConfig({
type: 'form',
labelPosition: 'top',
rowGutter: 16,
labelWidth: 0,
column: 2,
size: 'middle',
body: [
{
prop: 'name',
label: '申请人',
type: 'input',
disabled: true,
},
{
prop: 'department',
label: '申请人部门',
type: 'input',
disabled: true,
},
{
prop: 'companyCode',
label: '所属公司',
type: 'select',
required: true,
map: {
D802: '总部-D802',
F800: '总部-F800',
O801: '总部-O801',
O802: '子公司-O802',
O803: '子公司-O803',
O804: '子公司-O804',
O805: '子公司-O805',
},
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'applyCreditLimit',
label: '信贷额度',
type: 'input',
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'applyPaymentDays',
label: '付款日',
type: 'input',
disabled() {
return props.type !== FLOW_TASK_TYPE_CREATE
},
},
{
prop: 'comment',
label: '签字意见',
type: 'textarea',
rows: 2,
required: true,
colSpan: 2,
visible() {
return props.type !== FLOW_TASK_TYPE_VIEW
},
},
],
})
function getFormData() {
return new Promise((resolve, reject) => {
formRef.value
.validate()
.then(formData => {
resolve(formData)
})
.catch(() => {
reject()
})
})
}
defineExpose({ getFormData })
</script>@gauss/flowable Export
flowableRoutes
路由配置
locales
国际化配置
locales
└── zh
└── enlocales
└── zh
└── enprovide
注册表单组件,建议在 main.js 内注册
js
provide({ TaskForm })provide({ TaskForm })taskFormProps
表单组件 props
js
export const taskFormProps = {
type: {
type: String,
},
template: {
type: Object,
},
taskNodes: {
type: Object,
},
getApplyInfo: {
type: Function,
default: async () => {},
},
}export const taskFormProps = {
type: {
type: String,
},
template: {
type: Object,
},
taskNodes: {
type: Object,
},
getApplyInfo: {
type: Function,
default: async () => {},
},
}任务状态
FLOW_TASK_TYPE_CREATE 创建状态
FLOW_TASK_TYPE_HANDLE 处理中
FLOW_TASK_TYPE_VIEW 只读
定制任务处理表单
TaskForm
接入方需要实现一个表单组件,用来替换任务处理页面默认的表单,并 expose一个getFormData方法让父组件获取需要提交的表单数据。
html
<template>
<el-form ref="formRef" :model="formData">
<template v-if="type === 'create'">
<!-- 创建任务 -->
</template>
<template v-else-if="type === 'handle'">
<!-- 处理任务 -->
</template>
<template v-else-if="type === 'view'">
<!-- 任务详情 -->
</template>
<el-form-item>
<el-input v-model="comment" />
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { defineProps, defineExpose, ref, reactive } from 'vue'
import { FormInstance } from 'element-plus'
const props = defineProps({
type: {
type: String as 'create' | 'handle' | 'view',
},
taskNodes: {
type: Object as ITaskNodeInfo,
},
template: {
type: Object as ITemplate,
},
getApplyInfo: {
type: Function as () => Promise<IApplyInfo>,
},
})
const formRef = ref<FormInstance>(null)
const comment = ref('')
const formData = reactive({})
async function getFormData() {
if (formRef.value) {
await formRef.value.validate()
return {
applyForm: formData,
comment: comment.value,
}
}
return Promise.reject(new Error('xxx'))
}
</script><template>
<el-form ref="formRef" :model="formData">
<template v-if="type === 'create'">
<!-- 创建任务 -->
</template>
<template v-else-if="type === 'handle'">
<!-- 处理任务 -->
</template>
<template v-else-if="type === 'view'">
<!-- 任务详情 -->
</template>
<el-form-item>
<el-input v-model="comment" />
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { defineProps, defineExpose, ref, reactive } from 'vue'
import { FormInstance } from 'element-plus'
const props = defineProps({
type: {
type: String as 'create' | 'handle' | 'view',
},
taskNodes: {
type: Object as ITaskNodeInfo,
},
template: {
type: Object as ITemplate,
},
getApplyInfo: {
type: Function as () => Promise<IApplyInfo>,
},
})
const formRef = ref<FormInstance>(null)
const comment = ref('')
const formData = reactive({})
async function getFormData() {
if (formRef.value) {
await formRef.value.validate()
return {
applyForm: formData,
comment: comment.value,
}
}
return Promise.reject(new Error('xxx'))
}
</script>Props
| 属性名 | 说明 | 类型 |
|---|---|---|
type | 任务类型 | 'create' / 'handle' / 'view' |
template | 查询流程模板基本信息 | ITemplate |
taskNodes | 获取流程节点的配置信息,已办任务无 | ITaskNodeInfo |
getApplyInfo | 获取创建流程时提交的申请信息 | () => Promise<IApplyInfo> |
Expose
| 属性名 | 说明 | 类型 |
|---|---|---|
getFormData | 执行表单校验,并返回需要提交的表单信息 | () => Promise<ISubmitPayload> |
Interface
ts
interface IApplyInfo {
applyInfo: Record<string, unknown> // 申请信息
isEdit: boolean // 表单是否可编辑,驳回到申请节点时返回true
}
interface ISubmitPayload {
comment: string // 审批意见
applyInfo: Record<string, unknown> // 申请信息
}
interface ITaskNodeInfo {
firstKey: string // 第一个节点key
currentNodeKey?: string // 当前节点key
node: {
// 节点配置数组
key: string // 节点key
extensionProp: string // 节点配置值
}[]
}
interface ITemplate {
category: string // 流程分类
categoryName: string // 流程分类
id: string // 模板id
name: string // 模板名称
key: string // 模板标识
version: string // 模板版本
}interface IApplyInfo {
applyInfo: Record<string, unknown> // 申请信息
isEdit: boolean // 表单是否可编辑,驳回到申请节点时返回true
}
interface ISubmitPayload {
comment: string // 审批意见
applyInfo: Record<string, unknown> // 申请信息
}
interface ITaskNodeInfo {
firstKey: string // 第一个节点key
currentNodeKey?: string // 当前节点key
node: {
// 节点配置数组
key: string // 节点key
extensionProp: string // 节点配置值
}[]
}
interface ITemplate {
category: string // 流程分类
categoryName: string // 流程分类
id: string // 模板id
name: string // 模板名称
key: string // 模板标识
version: string // 模板版本
}TaskGateway
当业务接入方需要对任务创建、处理、查看、批注页面进行定制时可以使用 TaskGateway 组件,此组件集成了任务生命周期中的所有功能,并支持业务接入方根据自身需求对界面进行一定程序的定制和扩展。
使用示例
html
<template>
<task-gateway type="create" ref="taskGatewayRef" :externalTabPanes="externalTabPanes">
<template #taskForm>
<task-form ref="taskFormRef" />
</template>
<template #taskFormActions>
<el-button @click="createTask">提交</el-button>
<el-button @click="saveDraft">保存草稿</el-button>
</template>
</task-gateway>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { TaskGateway } from '@guass/flowable'
const taskGatewayRef = ref<ITaskGatewayInstance>()
const taskFormRef = ref<{ getFormData({ businessType: 'create' | 'agree' | 'reject' }): Promise<ISubmitPayload> }>()
const externalTabPanes = [{ label: 'label', name: 'name', content: 'content' }]
async function createTask() {
const formData = await taskFormRef.value.getFormData({ businessType: 'create' })
taskGatewayRef.value.createTask(formData)
}
async function saveDraft() {
const formData = await taskFormRef.value.getFormData()
// 业务接入方实现保存草稿接口
}
</script><template>
<task-gateway type="create" ref="taskGatewayRef" :externalTabPanes="externalTabPanes">
<template #taskForm>
<task-form ref="taskFormRef" />
</template>
<template #taskFormActions>
<el-button @click="createTask">提交</el-button>
<el-button @click="saveDraft">保存草稿</el-button>
</template>
</task-gateway>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { TaskGateway } from '@guass/flowable'
const taskGatewayRef = ref<ITaskGatewayInstance>()
const taskFormRef = ref<{ getFormData({ businessType: 'create' | 'agree' | 'reject' }): Promise<ISubmitPayload> }>()
const externalTabPanes = [{ label: 'label', name: 'name', content: 'content' }]
async function createTask() {
const formData = await taskFormRef.value.getFormData({ businessType: 'create' })
taskGatewayRef.value.createTask(formData)
}
async function saveDraft() {
const formData = await taskFormRef.value.getFormData()
// 业务接入方实现保存草稿接口
}
</script>属性 Props
| 属性名 | 描述 | 类型 | 默认值 |
|---|---|---|---|
type | 类型 | - | |
procInsId | 流程实例id | string | - |
procDefId | 流程定义id | string | - |
deployId | 流程模板部署id | string | - |
taskId | 任务id | string | - |
allowCreate | 允许创建 | boolean | true |
allowAgree | 允许同意 | boolean | true |
allowReject | 允许驳回 | boolean | true |
allowAssign | 允许转办 | boolean | true |
allowCced | 允许抄送 | boolean | true |
allowDelegate | 允许助审 | boolean | true |
allowRemark | 允许批注 | boolean | true |
rejectResubmitTarget | 驳回后再次提交目标 | IRejectResubmitTarget | { visible: true, defaultValue: 'defaultNode' } |
showProcessRecords | 显示流转记录 | boolean | true |
showProcessGraph | 显示流程图 | boolean | true |
showFormHeader | 显示表单header | boolean | true |
showBack | 显示返回按钮 | boolean | true |
goBackUrl | 操作完成后返回哪个页面 | string | - |
externalTabPanes | 扩展选项卡 | IExternalTabPane[] | - |
buttonPlacement | 表单操作按钮的位置 | 'bottom' | 'top-right' | bottom |
buttonWrapClass | 为按钮组的容器添加类名,仅当buttonPlacement为top-right时有效 | string | - |
rejectSetting | 驳回时是否弹出驳回设置弹窗 | boolean | true |
事件 Events
| 事件名 | 描述 | 回调参数 |
|---|---|---|
onSubmit | 任务提交回调 | - |
onAgree | 任务审批回调 | - |
onReject | 任务驳回回调 | - |
方法 Expose
| 方法名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
getTaskFormRef | 返回表单组件的引用 | - | Record<string, unknown> |
getApplyInfo | 返回当前任务的创建信息 | - | IApplyInfo |
getTaskNodeInfo | 返回当前流程的节点配置信息 | - | ITaskNodeInfo |
getTemplateInfo | 返回流程模板信息 | - | ITemplate |
createTask | 创建任务(发起流程) | ISubmitPayload | - |
agreeTask | 同意任务 | Pick<ISubmitPayload, 'applyInfo' | 'comment'> | - |
rejectTask | 驳回任务 | Pick<ISubmitPayload,'comment'> | - |
插槽 Slots
| 插槽名 | 描述 |
|---|---|
taskForm | 表单组件 |
taskFormActions | 任务操作按钮 |
类型 Types
ts
interface ISubmitPayload {
applyInfo: Record<string, unknown> // 申请信息
comment: string // 审批意见
businessId?: string // 业务标识
skipUrl?: string // 指定在流程列表点击“业务标识”字段时的跳转地址
}
interface IApplyInfo {
applyInfo: Record<string, unknwon> // 申请信息
isEdit: boolean // 是否在申请节点(驳回到申请节点)
}
interface ITaskNodeInfo {
firstKey: string // 第一个节点key
currentNodeKey?: string // 当前节点key
node: {
key: string // 节点key
extensionProp: string // 节点配置值
}[] // 节点配置数组
}
interface ITemplate {
category: string // 流程分类
categoryName: string // 流程分类
id: string // 模板id
name: string // 模板名称
key: string // 模板标识
version: string // 模板版本
}
interface IExternalTabPane {
label: string
name: string
content: any
}
interface IRejectResubmitTarget {
visible?: boolean
defaultValue?: 'defaultNode' | 'rejectNode'
}interface ISubmitPayload {
applyInfo: Record<string, unknown> // 申请信息
comment: string // 审批意见
businessId?: string // 业务标识
skipUrl?: string // 指定在流程列表点击“业务标识”字段时的跳转地址
}
interface IApplyInfo {
applyInfo: Record<string, unknwon> // 申请信息
isEdit: boolean // 是否在申请节点(驳回到申请节点)
}
interface ITaskNodeInfo {
firstKey: string // 第一个节点key
currentNodeKey?: string // 当前节点key
node: {
key: string // 节点key
extensionProp: string // 节点配置值
}[] // 节点配置数组
}
interface ITemplate {
category: string // 流程分类
categoryName: string // 流程分类
id: string // 模板id
name: string // 模板名称
key: string // 模板标识
version: string // 模板版本
}
interface IExternalTabPane {
label: string
name: string
content: any
}
interface IRejectResubmitTarget {
visible?: boolean
defaultValue?: 'defaultNode' | 'rejectNode'
}
Gauss