refactor: 重构 bpmnProcessDesigner 组件 ele => antd
This commit is contained in:
@@ -3,112 +3,135 @@
|
||||
class="process-panel__container"
|
||||
:style="{ width: `${width}px`, maxHeight: '600px' }"
|
||||
>
|
||||
<el-collapse v-model="activeTab" v-if="isReady">
|
||||
<el-collapse-item name="base">
|
||||
<!-- class="panel-tab__title" -->
|
||||
<Collapse v-model:activeKey="activeTab" v-if="isReady">
|
||||
<CollapsePanel key="base" header="常规">
|
||||
<template #title>
|
||||
<Icon icon="ep:info-filled" />
|
||||
常规</template
|
||||
>
|
||||
<Icon icon="ant-design:info-circle-filled" />
|
||||
常规
|
||||
</template>
|
||||
<ElementBaseInfo
|
||||
:id-edit-disabled="idEditDisabled"
|
||||
:business-object="elementBusinessObject"
|
||||
:type="elementType"
|
||||
:model="model"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
name="condition"
|
||||
v-if="elementType === 'Process'"
|
||||
</CollapsePanel>
|
||||
<CollapsePanel
|
||||
key="message"
|
||||
v-if="elementType === 'Process'"
|
||||
>
|
||||
<template #title><Icon icon="ep:comment" />消息与信号</template>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:message-filled" />
|
||||
消息与信号
|
||||
</template>
|
||||
<signal-and-massage />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
name="condition"
|
||||
v-if="conditionFormVisible"
|
||||
</CollapsePanel>
|
||||
<CollapsePanel
|
||||
key="condition"
|
||||
v-if="conditionFormVisible"
|
||||
>
|
||||
<template #title><Icon icon="ep:promotion" />流转条件</template>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:swap-right" />
|
||||
流转条件
|
||||
</template>
|
||||
<flow-condition
|
||||
:business-object="elementBusinessObject"
|
||||
:type="elementType"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="condition" v-if="formVisible" key="form">
|
||||
<template #title><Icon icon="ep:list" />表单</template>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel key="form" v-if="formVisible">
|
||||
<template #title>
|
||||
<Icon icon="ant-design:unordered-list-outlined" />
|
||||
表单
|
||||
</template>
|
||||
<element-form :id="elementId" :type="elementType" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
name="task"
|
||||
v-if="isTaskCollapseItemShow(elementType)"
|
||||
</CollapsePanel>
|
||||
<CollapsePanel
|
||||
key="task"
|
||||
v-if="isTaskCollapseItemShow(elementType)"
|
||||
>
|
||||
<template #title
|
||||
><Icon icon="ep:checked" />{{
|
||||
getTaskCollapseItemName(elementType)
|
||||
}}</template
|
||||
>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:check-circle-filled" />
|
||||
{{ getTaskCollapseItemName(elementType) }}
|
||||
</template>
|
||||
<element-task :id="elementId" :type="elementType" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
name="multiInstance"
|
||||
v-if="elementType.indexOf('Task') !== -1"
|
||||
</CollapsePanel>
|
||||
<CollapsePanel
|
||||
key="multiInstance"
|
||||
v-if="elementType.indexOf('Task') !== -1"
|
||||
>
|
||||
<template #title><Icon icon="ep:help-filled" />多人审批方式</template>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:question-circle-filled" />
|
||||
多人审批方式
|
||||
</template>
|
||||
<element-multi-instance
|
||||
:id="elementId"
|
||||
:business-object="elementBusinessObject"
|
||||
:type="elementType"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="listeners" key="listeners">
|
||||
<template #title><Icon icon="ep:bell-filled" />执行监听器</template>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel key="listeners">
|
||||
<template #title>
|
||||
<Icon icon="ant-design:bell-filled" />
|
||||
执行监听器
|
||||
</template>
|
||||
<element-listeners :id="elementId" :type="elementType" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
name="taskListeners"
|
||||
v-if="elementType === 'UserTask'"
|
||||
</CollapsePanel>
|
||||
<CollapsePanel
|
||||
key="taskListeners"
|
||||
v-if="elementType === 'UserTask'"
|
||||
>
|
||||
<template #title><Icon icon="ep:bell-filled" />任务监听器</template>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:bell-filled" />
|
||||
任务监听器
|
||||
</template>
|
||||
<user-task-listeners :id="elementId" :type="elementType" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="extensions" key="extensions">
|
||||
<template #title
|
||||
><Icon icon="ep:circle-plus-filled" />扩展属性</template
|
||||
>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel key="extensions">
|
||||
<template #title>
|
||||
<Icon icon="ant-design:plus-circle-filled" />
|
||||
扩展属性
|
||||
</template>
|
||||
<element-properties :id="elementId" :type="elementType" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="other" key="other">
|
||||
<template #title><Icon icon="ep:promotion" />其他</template>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel key="other">
|
||||
<template #title>
|
||||
<Icon icon="ant-design:swap-right" />
|
||||
其他
|
||||
</template>
|
||||
<element-other-config :id="elementId" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item name="customConfig" key="customConfig">
|
||||
<template #title><Icon icon="ep:tools" />自定义配置</template>
|
||||
</CollapsePanel>
|
||||
<CollapsePanel key="customConfig">
|
||||
<template #title>
|
||||
<Icon icon="ant-design:tool-filled" />
|
||||
自定义配置
|
||||
</template>
|
||||
<element-custom-config
|
||||
:id="elementId"
|
||||
:type="elementType"
|
||||
:business-object="elementBusinessObject"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
</CollapsePanel>
|
||||
<!-- 新增的时间事件配置项 -->
|
||||
<el-collapse-item
|
||||
<CollapsePanel
|
||||
key="timeEvent"
|
||||
v-if="elementType === 'IntermediateCatchEvent'"
|
||||
name="timeEvent"
|
||||
>
|
||||
<template #title><Icon icon="ep:timer" />时间事件</template>
|
||||
<template #title>
|
||||
<Icon icon="ant-design:clock-circle-filled" />
|
||||
时间事件
|
||||
</template>
|
||||
<TimeEventConfig
|
||||
:businessObject="bpmnElement.value?.businessObject"
|
||||
:key="elementId"
|
||||
/>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</CollapsePanel>
|
||||
</Collapse>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Collapse, CollapsePanel } from 'ant-design-vue';
|
||||
import { Icon } from '@vben/icons';
|
||||
import ElementBaseInfo from './base/ElementBaseInfo.vue';
|
||||
import ElementOtherConfig from './other/ElementOtherConfig.vue';
|
||||
import ElementTask from './task/ElementTask.vue';
|
||||
@@ -121,7 +144,7 @@ import ElementProperties from './properties/ElementProperties.vue';
|
||||
import UserTaskListeners from './listeners/UserTaskListeners.vue';
|
||||
import { getTaskCollapseItemName, isTaskCollapseItemShow } from './task/data';
|
||||
import TimeEventConfig from './time-event-config/TimeEventConfig.vue';
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { ref, watch, onMounted, onBeforeUnmount, provide, nextTick } from 'vue';
|
||||
|
||||
defineOptions({ name: 'MyPropertiesPanel' });
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { onBeforeUnmount, reactive, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { Form, FormItem, Input } from 'ant-design-vue';
|
||||
import type { FormInstance, Rule } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ElementBaseInfo' });
|
||||
|
||||
@@ -23,6 +24,7 @@ interface Model {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const needProps = ref<Record<string, any>>({});
|
||||
const bpmnElement = ref<any>();
|
||||
const elementBaseInfo = ref<BusinessObject>({} as any);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { defineOptions, defineProps, ref, watch } from 'vue';
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import { CustomConfigMap } from './data';
|
||||
|
||||
@@ -26,7 +27,7 @@ interface BusinessObject {
|
||||
}
|
||||
|
||||
// const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
const customConfigComponent = ref<any>(null);
|
||||
const customConfigComponent = ref<Component | null>(null);
|
||||
|
||||
watch(
|
||||
() => props.businessObject,
|
||||
@@ -37,7 +38,7 @@ watch(
|
||||
val +=
|
||||
props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || '';
|
||||
}
|
||||
customConfigComponent.value = (CustomConfigMap as any)[val]?.component;
|
||||
customConfigComponent.value = (CustomConfigMap as Record<string, { component: Component }>)[val]?.component;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
|
||||
@@ -12,9 +12,13 @@ import {
|
||||
import {
|
||||
Divider,
|
||||
Form,
|
||||
FormItem,
|
||||
InputNumber,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
RadioButton,
|
||||
Select,
|
||||
SelectOption,
|
||||
Switch,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
@@ -223,35 +227,35 @@ watch(
|
||||
<template>
|
||||
<div>
|
||||
<Divider orientation="left">审批人超时未处理时</Divider>
|
||||
<Form.Item label="启用开关" name="timeoutHandlerEnable">
|
||||
<FormItem label="启用开关" name="timeoutHandlerEnable">
|
||||
<Switch
|
||||
v-model:checked="timeoutHandlerEnable"
|
||||
checked-children="开启"
|
||||
un-checked-children="关闭"
|
||||
@change="timeoutHandlerChange"
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="执行动作"
|
||||
name="timeoutHandlerType"
|
||||
v-if="timeoutHandlerEnable"
|
||||
>
|
||||
<Radio.Group
|
||||
<RadioGroup
|
||||
v-model:value="timeoutHandlerType.value"
|
||||
@change="onTimeoutHandlerTypeChanged"
|
||||
>
|
||||
<Radio.Button
|
||||
<RadioButton
|
||||
v-for="item in TIMEOUT_HANDLER_TYPES"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
<Form.Item label="超时时间设置" v-if="timeoutHandlerEnable">
|
||||
</RadioButton>
|
||||
</RadioGroup>
|
||||
</FormItem>
|
||||
<FormItem label="超时时间设置" v-if="timeoutHandlerEnable">
|
||||
<span class="mr-2">当超过</span>
|
||||
<Form.Item name="timeDuration">
|
||||
<FormItem name="timeDuration">
|
||||
<InputNumber
|
||||
class="mr-2"
|
||||
:style="{ width: '100px' }"
|
||||
@@ -265,24 +269,24 @@ watch(
|
||||
}
|
||||
"
|
||||
/>
|
||||
</Form.Item>
|
||||
</FormItem>
|
||||
<Select
|
||||
v-model:value="timeUnit"
|
||||
class="mr-2"
|
||||
:style="{ width: '100px' }"
|
||||
@change="onTimeUnitChange"
|
||||
>
|
||||
<Select.Option
|
||||
<SelectOption
|
||||
v-for="item in TIME_UNIT_TYPES"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</Select.Option>
|
||||
</SelectOption>
|
||||
</Select>
|
||||
未处理
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="最大提醒次数"
|
||||
name="maxRemindCount"
|
||||
v-if="timeoutHandlerEnable && timeoutHandlerType.value === 1"
|
||||
@@ -298,7 +302,7 @@ watch(
|
||||
}
|
||||
"
|
||||
/>
|
||||
</Form.Item>
|
||||
</FormItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ onMounted(async () => {
|
||||
<template>
|
||||
<div>
|
||||
<Divider orientation="left">审批类型</Divider>
|
||||
<Form.Item prop="approveType">
|
||||
<Form.Item name="approveType" label="审批类型">
|
||||
<RadioGroup v-model:value="approveType.value">
|
||||
<Radio
|
||||
v-for="(item, index) in APPROVE_TYPE"
|
||||
@@ -411,7 +411,7 @@ onMounted(async () => {
|
||||
</Form.Item>
|
||||
|
||||
<Divider orientation="left">审批人拒绝时</Divider>
|
||||
<Form.Item prop="rejectHandlerType">
|
||||
<Form.Item name="rejectHandlerType" label="处理方式">
|
||||
<RadioGroup
|
||||
v-model:value="rejectHandlerType"
|
||||
:disabled="returnTaskList.length === 0"
|
||||
@@ -428,14 +428,15 @@ onMounted(async () => {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
v-if="rejectHandlerType === RejectHandlerType.RETURN_USER_TASK"
|
||||
name="returnNodeId"
|
||||
label="驳回节点"
|
||||
prop="returnNodeId"
|
||||
>
|
||||
<Select
|
||||
v-model:value="returnNodeId"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="updateReturnNodeId"
|
||||
placeholder="请选择驳回节点"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in returnTaskList"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { Form, Input, Select } from 'ant-design-vue';
|
||||
const { TextArea } = Input;
|
||||
|
||||
defineOptions({ name: 'FlowCondition' });
|
||||
|
||||
@@ -212,9 +213,9 @@ watch(
|
||||
v-if="flowConditionForm.scriptType === 'inlineScript'"
|
||||
key="body"
|
||||
>
|
||||
<Input
|
||||
<TextArea
|
||||
v-model:value="flowConditionForm.body"
|
||||
:autosize="{ minRows: 2, maxRows: 6 }"
|
||||
:auto-size="{ minRows: 2, maxRows: 6 }"
|
||||
allow-clear
|
||||
@change="updateFlowCondition"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, nextTick, onMounted, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { Form, FormItem, Select } from 'ant-design-vue';
|
||||
import { Form, FormItem, Select, Table, TableColumn, Button, Divider, Drawer, Input, Modal } from 'ant-design-vue';
|
||||
|
||||
import { getFormSimpleList } from '#/api/bpm/form';
|
||||
|
||||
@@ -313,12 +313,16 @@ watch(
|
||||
:options="formOptions"
|
||||
/>
|
||||
</FormItem>
|
||||
<!-- <FormItem label="业务标识">-->
|
||||
<!-- <Select v-model:value="businessKey" @change="updateElementBusinessKey">-->
|
||||
<!-- <SelectOption v-for="i in fieldList" :key="i.id" :value="i.id">{{ i.label }}</SelectOption>-->
|
||||
<!-- <SelectOption value="">无</SelectOption>-->
|
||||
<!-- </Select>-->
|
||||
<!-- </FormItem>-->
|
||||
<FormItem label="业务标识">
|
||||
<Select
|
||||
v-model:value="businessKey"
|
||||
@change="_updateElementBusinessKey"
|
||||
allow-clear
|
||||
>
|
||||
<Select.Option v-for="i in fieldList" :key="i.id" :value="i.id">{{ i.label }}</Select.Option>
|
||||
<Select.Option value="">无</Select.Option>
|
||||
</Select>
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
<!--字段列表-->
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { inject, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import {
|
||||
IconifyIcon,
|
||||
MenuOutlined,
|
||||
PlusOutlined,
|
||||
SelectOutlined,
|
||||
} from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
@@ -288,7 +293,7 @@ watch(
|
||||
<div class="element-drawer__button">
|
||||
<Button type="primary" size="small" @click="openListenerForm(null, -1)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加监听器
|
||||
</Button>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { reactive, ref } from 'vue';
|
||||
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
|
||||
import { Button, Modal, Pagination, Table, TableColumn } from 'ant-design-vue';
|
||||
import { Button, Modal, Pagination, Table } from 'ant-design-vue';
|
||||
|
||||
import { getProcessListenerPage } from '#/api/bpm/processListener';
|
||||
import { ContentWrap } from '#/components/content-wrap';
|
||||
@@ -71,30 +71,30 @@ const select = async (row: BpmProcessListenerApi.ProcessListener) => {
|
||||
:pagination="false"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<TableColumn title="名字" align="center" data-index="name" />
|
||||
<TableColumn title="类型" align="center" data-index="type">
|
||||
<Table.Column title="名字" align="center" dataIndex="name" />
|
||||
<Table.Column title="类型" align="center" dataIndex="type">
|
||||
<template #default="{ record }">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE"
|
||||
:value="record.type"
|
||||
/>
|
||||
</template>
|
||||
</TableColumn>
|
||||
<TableColumn title="事件" align="center" data-index="event" />
|
||||
<TableColumn title="值类型" align="center" data-index="valueType">
|
||||
</Table.Column>
|
||||
<Table.Column title="事件" align="center" dataIndex="event" />
|
||||
<Table.Column title="值类型" align="center" dataIndex="valueType">
|
||||
<template #default="{ record }">
|
||||
<DictTag
|
||||
:type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
|
||||
:value="record.valueType"
|
||||
/>
|
||||
</template>
|
||||
</TableColumn>
|
||||
<TableColumn title="值" align="center" data-index="value" />
|
||||
<TableColumn title="操作" align="center">
|
||||
</Table.Column>
|
||||
<Table.Column title="值" align="center" dataIndex="value" />
|
||||
<Table.Column title="操作" align="center">
|
||||
<template #default="{ record }">
|
||||
<Button type="primary" @click="select(record)"> 选择 </Button>
|
||||
</template>
|
||||
</TableColumn>
|
||||
</Table.Column>
|
||||
</Table>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
|
||||
@@ -1,91 +1,44 @@
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<el-table :data="elementPropertyList" max-height="240" fit border>
|
||||
<el-table-column label="序号" width="50px" type="index" />
|
||||
<el-table-column
|
||||
label="属性名"
|
||||
prop="name"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="属性值"
|
||||
prop="value"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="110px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
@click="openAttributesForm(scope.row, scope.$index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
style="color: #ff4d4f"
|
||||
@click="removeAttributes(scope.row, scope.$index)"
|
||||
>
|
||||
移除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="element-drawer__button">
|
||||
<XButton
|
||||
type="primary"
|
||||
preIcon="ep:plus"
|
||||
title="添加属性"
|
||||
@click="openAttributesForm(null, -1)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="propertyFormModelVisible"
|
||||
title="属性配置"
|
||||
width="600px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="propertyForm" label-width="80px" ref="attributeFormRef">
|
||||
<el-form-item label="属性名:" prop="name">
|
||||
<el-input v-model="propertyForm.name" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="属性值:" prop="value">
|
||||
<el-input v-model="propertyForm.value" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="propertyFormModelVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="saveAttribute">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { inject, nextTick, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Modal,
|
||||
Table,
|
||||
TableColumn,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ElementProperties' });
|
||||
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const prefix = inject('prefix');
|
||||
// const width = inject('width')
|
||||
|
||||
const elementPropertyList = ref<any[]>([]);
|
||||
const propertyForm = ref<any>({});
|
||||
const elementPropertyList = ref<Array<{ name: string; value: string }>>([]);
|
||||
const propertyForm = ref<{ name?: string; value?: string }>({});
|
||||
const editingPropertyIndex = ref(-1);
|
||||
const propertyFormModelVisible = ref(false);
|
||||
const bpmnElement = ref();
|
||||
const otherExtensionList = ref();
|
||||
const bpmnElementProperties = ref();
|
||||
const bpmnElementPropertyList = ref();
|
||||
const attributeFormRef = ref();
|
||||
const bpmnElement = ref<any>();
|
||||
const otherExtensionList = ref<any[]>([]);
|
||||
const bpmnElementProperties = ref<any[]>([]);
|
||||
const bpmnElementPropertyList = ref<any[]>([]);
|
||||
const attributeFormRef = ref<any>();
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
const resetAttributesList = () => {
|
||||
@@ -94,7 +47,7 @@ const resetAttributesList = () => {
|
||||
bpmnElementProperties.value =
|
||||
// bpmnElement.value.businessObject?.extensionElements?.filter((ex) => {
|
||||
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||
(ex) => {
|
||||
(ex: any) => {
|
||||
if (ex.$type !== `${prefix}:Properties`) {
|
||||
otherExtensionList.value.push(ex);
|
||||
}
|
||||
@@ -103,30 +56,38 @@ const resetAttributesList = () => {
|
||||
) ?? [];
|
||||
|
||||
// 保存所有的 扩展属性字段
|
||||
bpmnElementPropertyList.value = bpmnElementProperties.value.reduce(
|
||||
(pre, current) => pre.concat(current.values),
|
||||
[],
|
||||
bpmnElementPropertyList.value = bpmnElementProperties.value.flatMap(
|
||||
(current: any) => current.values,
|
||||
);
|
||||
// 复制 显示
|
||||
elementPropertyList.value = JSON.parse(
|
||||
JSON.stringify(bpmnElementPropertyList.value ?? []),
|
||||
elementPropertyList.value = structuredClone(
|
||||
bpmnElementPropertyList.value ?? [],
|
||||
);
|
||||
};
|
||||
const openAttributesForm = (attr, index) => {
|
||||
|
||||
const openAttributesForm = (
|
||||
attr: null | { name: string; value: string },
|
||||
index: number,
|
||||
) => {
|
||||
editingPropertyIndex.value = index;
|
||||
propertyForm.value = index === -1 ? {} : JSON.parse(JSON.stringify(attr));
|
||||
// @ts-ignore
|
||||
propertyForm.value = index === -1 ? {} : structuredClone(attr);
|
||||
propertyFormModelVisible.value = true;
|
||||
nextTick(() => {
|
||||
if (attributeFormRef.value) attributeFormRef.value.clearValidate();
|
||||
});
|
||||
};
|
||||
const removeAttributes = (attr, index) => {
|
||||
console.log(attr, 'attr');
|
||||
ElMessageBox.confirm('确认移除该属性吗?', '提示', {
|
||||
confirmButtonText: '确 认',
|
||||
cancelButtonText: '取 消',
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
const removeAttributes = (
|
||||
_attr: { name: string; value: string },
|
||||
index: number,
|
||||
) => {
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
content: '确认移除该属性吗?',
|
||||
okText: '确 认',
|
||||
cancelText: '取 消',
|
||||
onOk() {
|
||||
elementPropertyList.value.splice(index, 1);
|
||||
bpmnElementPropertyList.value.splice(index, 1);
|
||||
// 新建一个属性字段的保存列表
|
||||
@@ -138,22 +99,17 @@ const removeAttributes = (attr, index) => {
|
||||
);
|
||||
updateElementExtensions(propertiesObject);
|
||||
resetAttributesList();
|
||||
})
|
||||
.catch(() => console.info('操作取消'));
|
||||
},
|
||||
onCancel() {
|
||||
// console.info('操作取消');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const saveAttribute = () => {
|
||||
console.log(propertyForm.value, 'propertyForm.value');
|
||||
// console.log(propertyForm.value, 'propertyForm.value');
|
||||
const { name, value } = propertyForm.value;
|
||||
if (editingPropertyIndex.value !== -1) {
|
||||
bpmnInstances().modeling.updateModdleProperties(
|
||||
toRaw(bpmnElement.value),
|
||||
toRaw(bpmnElementPropertyList.value)[toRaw(editingPropertyIndex.value)],
|
||||
{
|
||||
name,
|
||||
value,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
if (editingPropertyIndex.value === -1) {
|
||||
// 新建属性字段
|
||||
const newPropertyObject = bpmnInstances().moddle.create(
|
||||
`${prefix}:Property`,
|
||||
@@ -166,17 +122,27 @@ const saveAttribute = () => {
|
||||
const propertiesObject = bpmnInstances().moddle.create(
|
||||
`${prefix}:Properties`,
|
||||
{
|
||||
values: bpmnElementPropertyList.value.concat([newPropertyObject]),
|
||||
values: [...bpmnElementPropertyList.value, newPropertyObject],
|
||||
},
|
||||
);
|
||||
updateElementExtensions(propertiesObject);
|
||||
} else {
|
||||
bpmnInstances().modeling.updateModdleProperties(
|
||||
toRaw(bpmnElement.value),
|
||||
toRaw(bpmnElementPropertyList.value)[toRaw(editingPropertyIndex.value)],
|
||||
{
|
||||
name,
|
||||
value,
|
||||
},
|
||||
);
|
||||
}
|
||||
propertyFormModelVisible.value = false;
|
||||
resetAttributesList();
|
||||
};
|
||||
const updateElementExtensions = (properties) => {
|
||||
|
||||
const updateElementExtensions = (properties: any) => {
|
||||
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||
values: otherExtensionList.value.concat([properties]),
|
||||
values: [...otherExtensionList.value, properties],
|
||||
});
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
extensionElements: extensions,
|
||||
@@ -187,9 +153,85 @@ watch(
|
||||
() => props.id,
|
||||
(val) => {
|
||||
if (val) {
|
||||
val && val.length && resetAttributesList();
|
||||
val && val.length > 0 && resetAttributesList();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<Table :data="elementPropertyList" :scroll="{ y: 240 }" bordered>
|
||||
<TableColumn title="序号" width="50">
|
||||
<template #default="{ index }">
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
</TableColumn>
|
||||
<TableColumn
|
||||
title="属性名"
|
||||
data-index="name"
|
||||
:min-width="100"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
<TableColumn
|
||||
title="属性值"
|
||||
data-index="value"
|
||||
:min-width="100"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
<TableColumn title="操作" width="110">
|
||||
<template #default="{ record, index }">
|
||||
<Button
|
||||
type="link"
|
||||
@click="openAttributesForm(record, index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="removeAttributes(record, index)"
|
||||
>
|
||||
移除
|
||||
</Button>
|
||||
</template>
|
||||
</TableColumn>
|
||||
</Table>
|
||||
<div class="element-drawer__button">
|
||||
<Button type="primary" @click="openAttributesForm(null, -1)">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</template>
|
||||
添加属性
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
v-model:open="propertyFormModelVisible"
|
||||
title="属性配置"
|
||||
:width="600"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<Form
|
||||
:model="propertyForm"
|
||||
ref="attributeFormRef"
|
||||
:label-col="{ span: 6 }"
|
||||
>
|
||||
<FormItem label="属性名:" name="name">
|
||||
<Input v-model:value="propertyForm.name" allow-clear />
|
||||
</FormItem>
|
||||
<FormItem label="属性值:" name="value">
|
||||
<Input v-model:value="propertyForm.value" allow-clear />
|
||||
</FormItem>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<Button @click="propertyFormModelVisible = false">取 消</Button>
|
||||
<Button type="primary" @click="saveAttribute">确 定</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,94 +1,20 @@
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<div class="panel-tab__content--title">
|
||||
<span
|
||||
><Icon
|
||||
icon="ep:menu"
|
||||
style="margin-right: 8px; color: #555"
|
||||
/>消息列表</span
|
||||
>
|
||||
<XButton
|
||||
type="primary"
|
||||
title="创建新消息"
|
||||
preIcon="ep:plus"
|
||||
@click="openModel('message')"
|
||||
/>
|
||||
</div>
|
||||
<el-table :data="messageList" border>
|
||||
<el-table-column type="index" label="序号" width="60px" />
|
||||
<el-table-column
|
||||
label="消息ID"
|
||||
prop="id"
|
||||
max-width="300px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="消息名称"
|
||||
prop="name"
|
||||
max-width="300px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</el-table>
|
||||
<div
|
||||
class="panel-tab__content--title"
|
||||
style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eee"
|
||||
>
|
||||
<span
|
||||
><Icon
|
||||
icon="ep:menu"
|
||||
style="margin-right: 8px; color: #555"
|
||||
/>信号列表</span
|
||||
>
|
||||
<XButton
|
||||
type="primary"
|
||||
title="创建新信号"
|
||||
preIcon="ep:plus"
|
||||
@click="openModel('signal')"
|
||||
/>
|
||||
</div>
|
||||
<el-table :data="signalList" border>
|
||||
<el-table-column type="index" label="序号" width="60px" />
|
||||
<el-table-column
|
||||
label="信号ID"
|
||||
prop="id"
|
||||
max-width="300px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="信号名称"
|
||||
prop="name"
|
||||
max-width="300px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
</el-table>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="modelConfig.title"
|
||||
:close-on-click-modal="false"
|
||||
width="400px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="modelObjectForm" label-width="90px">
|
||||
<el-form-item :label="modelConfig.idLabel">
|
||||
<el-input v-model="modelObjectForm.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="modelConfig.nameLabel">
|
||||
<el-input v-model="modelObjectForm.name" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="addNewObject">保 存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
defineOptions({ name: 'SignalAndMassage' });
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
const message = useMessage();
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
message,
|
||||
Modal,
|
||||
Table,
|
||||
TableColumn,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'SignalAndMassage' });
|
||||
const signalList = ref<any[]>([]);
|
||||
const messageList = ref<any[]>([]);
|
||||
const dialogVisible = ref(false);
|
||||
@@ -98,22 +24,20 @@ const rootElements = ref();
|
||||
const messageIdMap = ref();
|
||||
const signalIdMap = ref();
|
||||
const modelConfig = computed(() => {
|
||||
if (modelType.value === 'message') {
|
||||
return { title: '创建消息', idLabel: '消息ID', nameLabel: '消息名称' };
|
||||
} else {
|
||||
return { title: '创建信号', idLabel: '信号ID', nameLabel: '信号名称' };
|
||||
}
|
||||
return modelType.value === 'message'
|
||||
? { title: '创建消息', idLabel: '消息ID', nameLabel: '消息名称' }
|
||||
: { title: '创建信号', idLabel: '信号ID', nameLabel: '信号名称' };
|
||||
});
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
const initDataList = () => {
|
||||
console.log(window, 'window');
|
||||
// console.log(window, 'window');
|
||||
rootElements.value = bpmnInstances().modeler.getDefinitions().rootElements;
|
||||
messageIdMap.value = {};
|
||||
signalIdMap.value = {};
|
||||
messageList.value = [];
|
||||
signalList.value = [];
|
||||
rootElements.value.forEach((el) => {
|
||||
rootElements.value.forEach((el: any) => {
|
||||
if (el.$type === 'bpmn:Message') {
|
||||
messageIdMap.value[el.id] = true;
|
||||
messageList.value.push({ ...el });
|
||||
@@ -124,7 +48,7 @@ const initDataList = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
const openModel = (type) => {
|
||||
const openModel = (type: any) => {
|
||||
modelType.value = type;
|
||||
modelObjectForm.value = {};
|
||||
dialogVisible.value = true;
|
||||
@@ -157,3 +81,98 @@ onMounted(() => {
|
||||
initDataList();
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<div class="panel-tab__content--title">
|
||||
<span>
|
||||
<IconifyIcon icon="ep:menu" style="margin-right: 8px; color: #555" />
|
||||
消息列表
|
||||
</span>
|
||||
<Button type="primary" title="创建新消息" @click="openModel('message')">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</template>
|
||||
创建新消息
|
||||
</Button>
|
||||
</div>
|
||||
<Table :data-source="messageList" :bordered="true" :pagination="false">
|
||||
<TableColumn title="序号" width="60px">
|
||||
<template #default="{ index }">
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
</TableColumn>
|
||||
<TableColumn
|
||||
title="消息ID"
|
||||
data-index="id"
|
||||
:width="300"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
<TableColumn
|
||||
title="消息名称"
|
||||
data-index="name"
|
||||
:width="300"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
</Table>
|
||||
<div
|
||||
class="panel-tab__content--title"
|
||||
style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eee"
|
||||
>
|
||||
<span>
|
||||
<IconifyIcon icon="ep:menu" style="margin-right: 8px; color: #555">
|
||||
信号列表
|
||||
</IconifyIcon>
|
||||
</span>
|
||||
<Button type="primary" title="创建新信号" @click="openModel('signal')">
|
||||
<template #icon>
|
||||
<IconifyIcon icon="ep:plus" />
|
||||
</template>
|
||||
创建新信号
|
||||
</Button>
|
||||
</div>
|
||||
<Table :data-source="signalList" :bordered="true" :pagination="false">
|
||||
<TableColumn title="序号" width="60px">
|
||||
<template #default="{ index }">
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
</TableColumn>
|
||||
<TableColumn
|
||||
title="信号ID"
|
||||
data-index="id"
|
||||
:width="300"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
<TableColumn
|
||||
title="信号名称"
|
||||
data-index="name"
|
||||
:width="300"
|
||||
:ellipsis="{ showTitle: true }"
|
||||
/>
|
||||
</Table>
|
||||
|
||||
<Modal
|
||||
v-model:open="dialogVisible"
|
||||
:title="modelConfig.title"
|
||||
:mask-closable="false"
|
||||
width="400px"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<Form
|
||||
:model="modelObjectForm"
|
||||
:label-col="{ span: 9 }"
|
||||
:wrapper-col="{ span: 15 }"
|
||||
>
|
||||
<FormItem :label="modelConfig.idLabel">
|
||||
<Input v-model:value="modelObjectForm.id" allow-clear />
|
||||
</FormItem>
|
||||
<FormItem :label="modelConfig.nameLabel">
|
||||
<Input v-model:value="modelObjectForm.name" allow-clear />
|
||||
</FormItem>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<Button @click="dialogVisible = false">取 消</Button>
|
||||
<Button type="primary" @click="addNewObject">保 存</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<el-form size="small" label-width="90px">
|
||||
<Form :label-col="{ span: 9 }" :wrapper-col="{ span: 15 }">
|
||||
<!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
||||
<el-form-item label="异步延续" style="display: none">
|
||||
<el-checkbox
|
||||
v-model="taskConfigForm.asyncBefore"
|
||||
label="异步前"
|
||||
value="异步前"
|
||||
<Form.Item label="异步延续" style="display: none">
|
||||
<Checkbox
|
||||
v-model:checked="taskConfigForm.asyncBefore"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-model="taskConfigForm.asyncAfter"
|
||||
label="异步后"
|
||||
value="异步后"
|
||||
>
|
||||
异步前
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
v-model:checked="taskConfigForm.asyncAfter"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
<el-checkbox
|
||||
v-model="taskConfigForm.exclusive"
|
||||
>
|
||||
异步后
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
v-model:checked="taskConfigForm.exclusive"
|
||||
v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore"
|
||||
label="排除"
|
||||
value="排除"
|
||||
@change="changeTaskAsync"
|
||||
/>
|
||||
</el-form-item>
|
||||
>
|
||||
排除
|
||||
</Checkbox>
|
||||
</Form.Item>
|
||||
<component :is="witchTaskComponent" v-bind="$props" />
|
||||
</el-form>
|
||||
</Form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Form } from 'ant-design-vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import { installedComponent } from './data';
|
||||
|
||||
defineOptions({ name: 'ElementTaskConfig' });
|
||||
|
||||
@@ -27,10 +27,14 @@ export const installedComponent = {
|
||||
},
|
||||
};
|
||||
|
||||
export const getTaskCollapseItemName = (elementType) => {
|
||||
export const getTaskCollapseItemName = (
|
||||
elementType: keyof typeof installedComponent,
|
||||
) => {
|
||||
return installedComponent[elementType].name;
|
||||
};
|
||||
|
||||
export const isTaskCollapseItemShow = (elementType) => {
|
||||
export const isTaskCollapseItemShow = (
|
||||
elementType: keyof typeof installedComponent,
|
||||
) => {
|
||||
return installedComponent[elementType];
|
||||
};
|
||||
|
||||
@@ -1,189 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="实例名称" prop="processInstanceName">
|
||||
<el-input
|
||||
v-model="formData.processInstanceName"
|
||||
clearable
|
||||
placeholder="请输入实例名称"
|
||||
@change="updateCallActivityAttr('processInstanceName')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- TODO 需要可选择已存在的流程 -->
|
||||
<el-form-item label="被调用流程" prop="calledElement">
|
||||
<el-input
|
||||
v-model="formData.calledElement"
|
||||
clearable
|
||||
placeholder="请输入被调用流程"
|
||||
@change="updateCallActivityAttr('calledElement')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="继承变量" prop="inheritVariables">
|
||||
<el-switch
|
||||
v-model="formData.inheritVariables"
|
||||
@change="updateCallActivityAttr('inheritVariables')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="继承业务键" prop="inheritBusinessKey">
|
||||
<el-switch
|
||||
v-model="formData.inheritBusinessKey"
|
||||
@change="updateCallActivityAttr('inheritBusinessKey')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="!formData.inheritBusinessKey"
|
||||
label="业务键表达式"
|
||||
prop="businessKey"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.businessKey"
|
||||
clearable
|
||||
placeholder="请输入业务键表达式"
|
||||
@change="updateCallActivityAttr('businessKey')"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider />
|
||||
<div>
|
||||
<div class="mb-10px flex">
|
||||
<el-text>输入参数</el-text>
|
||||
<XButton
|
||||
class="ml-auto"
|
||||
type="primary"
|
||||
preIcon="ep:plus"
|
||||
title="添加参数"
|
||||
size="small"
|
||||
@click="openVariableForm('in', null, -1)"
|
||||
/>
|
||||
</div>
|
||||
<el-table :data="inVariableList" max-height="240" fit border>
|
||||
<el-table-column
|
||||
label="源"
|
||||
prop="source"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="目标"
|
||||
prop="target"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="110px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
@click="openVariableForm('in', scope.row, scope.$index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
style="color: #ff4d4f"
|
||||
@click="removeVariable('in', scope.$index)"
|
||||
>
|
||||
移除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
<div>
|
||||
<div class="mb-10px flex">
|
||||
<el-text>输出参数</el-text>
|
||||
<XButton
|
||||
class="ml-auto"
|
||||
type="primary"
|
||||
preIcon="ep:plus"
|
||||
title="添加参数"
|
||||
size="small"
|
||||
@click="openVariableForm('out', null, -1)"
|
||||
/>
|
||||
</div>
|
||||
<el-table :data="outVariableList" max-height="240" fit border>
|
||||
<el-table-column
|
||||
label="源"
|
||||
prop="source"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="目标"
|
||||
prop="target"
|
||||
min-width="100px"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="110px">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
@click="openVariableForm('out', scope.row, scope.$index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button
|
||||
link
|
||||
size="small"
|
||||
style="color: #ff4d4f"
|
||||
@click="removeVariable('out', scope.$index)"
|
||||
>
|
||||
移除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<!-- 添加或修改参数 -->
|
||||
<el-dialog
|
||||
v-model="variableDialogVisible"
|
||||
title="参数配置"
|
||||
width="600px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
:model="varialbeFormData"
|
||||
label-width="80px"
|
||||
ref="varialbeFormRef"
|
||||
>
|
||||
<el-form-item label="源:" prop="source">
|
||||
<el-input v-model="varialbeFormData.source" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="目标:" prop="target">
|
||||
<el-input v-model="varialbeFormData.target" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="variableDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="saveVariable">确 定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { inject, nextTick, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { alert } from '@vben/common-ui';
|
||||
import { PlusOutlined } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Divider,
|
||||
Form,
|
||||
FormItem,
|
||||
Input,
|
||||
Modal,
|
||||
Switch,
|
||||
Table,
|
||||
TableColumn,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
interface FormData {
|
||||
processInstanceName: string;
|
||||
calledElement: string;
|
||||
inheritVariables: boolean;
|
||||
businessKey: string;
|
||||
inheritBusinessKey: boolean;
|
||||
calledElementType: string;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'CallActivity' });
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: { type: String, default: '' },
|
||||
type: { type: String, default: '' },
|
||||
});
|
||||
const prefix = inject('prefix');
|
||||
const message = useMessage();
|
||||
|
||||
const formData = ref({
|
||||
const formData = ref<FormData>({
|
||||
processInstanceName: '',
|
||||
calledElement: '',
|
||||
inheritVariables: false,
|
||||
@@ -191,44 +40,51 @@ const formData = ref({
|
||||
inheritBusinessKey: false,
|
||||
calledElementType: 'key',
|
||||
});
|
||||
const inVariableList = ref();
|
||||
const outVariableList = ref();
|
||||
const variableType = ref(); // 参数类型
|
||||
const editingVariableIndex = ref(-1); // 编辑参数下标
|
||||
const variableDialogVisible = ref(false);
|
||||
const varialbeFormRef = ref();
|
||||
const varialbeFormData = ref({
|
||||
const inVariableList = ref<any[]>([]);
|
||||
const outVariableList = ref<any[]>([]);
|
||||
const variableType = ref<string>(); // 参数类型
|
||||
const editingVariableIndex = ref<number>(-1); // 编辑参数下标
|
||||
const variableDialogVisible = ref<boolean>(false);
|
||||
const varialbeFormRef = ref<any>();
|
||||
const varialbeFormData = ref<{
|
||||
source: string;
|
||||
target: string;
|
||||
}>({
|
||||
source: '',
|
||||
target: '',
|
||||
});
|
||||
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
const bpmnElement = ref();
|
||||
const otherExtensionList = ref();
|
||||
const bpmnElement = ref<any>();
|
||||
const otherExtensionList = ref<any[]>([]);
|
||||
|
||||
const initCallActivity = () => {
|
||||
bpmnElement.value = bpmnInstances().bpmnElement;
|
||||
console.log(bpmnElement.value.businessObject, 'callActivity');
|
||||
// console.log(bpmnElement.value.businessObject, 'callActivity');
|
||||
|
||||
// 初始化所有配置项
|
||||
Object.keys(formData.value).forEach((key) => {
|
||||
Object.keys(formData.value).forEach((key: string) => {
|
||||
// @ts-ignore
|
||||
formData.value[key] =
|
||||
bpmnElement.value.businessObject[key] ?? formData.value[key];
|
||||
bpmnElement.value.businessObject[key] ??
|
||||
formData.value[key as keyof FormData];
|
||||
});
|
||||
|
||||
otherExtensionList.value = []; // 其他扩展配置
|
||||
inVariableList.value = [];
|
||||
outVariableList.value = [];
|
||||
inVariableList.value.length = 0;
|
||||
outVariableList.value.length = 0;
|
||||
// 初始化输入参数
|
||||
bpmnElement.value.businessObject?.extensionElements?.values?.forEach((ex) => {
|
||||
if (ex.$type === `${prefix}:In`) {
|
||||
inVariableList.value.push(ex);
|
||||
} else if (ex.$type === `${prefix}:Out`) {
|
||||
outVariableList.value.push(ex);
|
||||
} else {
|
||||
otherExtensionList.value.push(ex);
|
||||
}
|
||||
});
|
||||
bpmnElement.value.businessObject?.extensionElements?.values?.forEach(
|
||||
(ex: any) => {
|
||||
if (ex.$type === `${prefix}:In`) {
|
||||
inVariableList.value.push(ex);
|
||||
} else if (ex.$type === `${prefix}:Out`) {
|
||||
outVariableList.value.push(ex);
|
||||
} else {
|
||||
otherExtensionList.value.push(ex);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 默认添加
|
||||
// bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
@@ -236,22 +92,22 @@ const initCallActivity = () => {
|
||||
// })
|
||||
};
|
||||
|
||||
const updateCallActivityAttr = (attr) => {
|
||||
const updateCallActivityAttr = (attr: keyof FormData) => {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
[attr]: formData.value[attr],
|
||||
});
|
||||
};
|
||||
|
||||
const openVariableForm = (type, data, index) => {
|
||||
const openVariableForm = (type: string, data: any, index: number) => {
|
||||
editingVariableIndex.value = index;
|
||||
variableType.value = type;
|
||||
varialbeFormData.value = index === -1 ? {} : { ...data };
|
||||
variableDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const removeVariable = async (type, index) => {
|
||||
const removeVariable = async (type: string, index: number) => {
|
||||
try {
|
||||
await message.delConfirm();
|
||||
await alert('是否确认删除?');
|
||||
if (type === 'in') {
|
||||
inVariableList.value.splice(index, 1);
|
||||
}
|
||||
@@ -313,7 +169,7 @@ watch(
|
||||
() => props.id,
|
||||
(val) => {
|
||||
val &&
|
||||
val.length &&
|
||||
val.length > 0 &&
|
||||
nextTick(() => {
|
||||
initCallActivity();
|
||||
});
|
||||
@@ -322,4 +178,184 @@ watch(
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<FormItem label="实例名称">
|
||||
<Input
|
||||
v-model:value="formData.processInstanceName"
|
||||
allow-clear
|
||||
placeholder="请输入实例名称"
|
||||
@change="updateCallActivityAttr('processInstanceName')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<!-- TODO 需要可选择已存在的流程 -->
|
||||
<FormItem label="被调用流程">
|
||||
<Input
|
||||
v-model:value="formData.calledElement"
|
||||
allow-clear
|
||||
placeholder="请输入被调用流程"
|
||||
@change="updateCallActivityAttr('calledElement')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="继承变量">
|
||||
<Switch
|
||||
v-model:checked="formData.inheritVariables"
|
||||
@change="updateCallActivityAttr('inheritVariables')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="继承业务键">
|
||||
<Switch
|
||||
v-model:checked="formData.inheritBusinessKey"
|
||||
@change="updateCallActivityAttr('inheritBusinessKey')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem v-if="!formData.inheritBusinessKey" label="业务键表达式">
|
||||
<Input
|
||||
v-model:value="formData.businessKey"
|
||||
allow-clear
|
||||
placeholder="请输入业务键表达式"
|
||||
@change="updateCallActivityAttr('businessKey')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<Divider />
|
||||
<div>
|
||||
<div class="mb-10px flex">
|
||||
<span>输入参数</span>
|
||||
<Button
|
||||
class="ml-auto"
|
||||
type="primary"
|
||||
:icon="PlusOutlined"
|
||||
title="添加参数"
|
||||
size="small"
|
||||
@click="openVariableForm('in', null, -1)"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:data-source="inVariableList"
|
||||
:scroll="{ y: 240 }"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<TableColumn
|
||||
title="源"
|
||||
data-index="source"
|
||||
:min-width="100"
|
||||
:ellipsis="true"
|
||||
/>
|
||||
<TableColumn
|
||||
title="目标"
|
||||
data-index="target"
|
||||
:min-width="100"
|
||||
:ellipsis="true"
|
||||
/>
|
||||
<TableColumn title="操作" :width="110">
|
||||
<template #default="{ record, index }">
|
||||
<Button
|
||||
type="link"
|
||||
@click="openVariableForm('in', record, index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="removeVariable('in', index)"
|
||||
>
|
||||
移除
|
||||
</Button>
|
||||
</template>
|
||||
</TableColumn>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<div>
|
||||
<div class="mb-10px flex">
|
||||
<span>输出参数</span>
|
||||
<Button
|
||||
class="ml-auto"
|
||||
type="primary"
|
||||
:icon="PlusOutlined"
|
||||
title="添加参数"
|
||||
size="small"
|
||||
@click="openVariableForm('out', null, -1)"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
:data-source="outVariableList"
|
||||
:scroll="{ y: 240 }"
|
||||
bordered
|
||||
:pagination="false"
|
||||
>
|
||||
<TableColumn
|
||||
title="源"
|
||||
data-index="source"
|
||||
:min-width="100"
|
||||
:ellipsis="true"
|
||||
/>
|
||||
<TableColumn
|
||||
title="目标"
|
||||
data-index="target"
|
||||
:min-width="100"
|
||||
:ellipsis="true"
|
||||
/>
|
||||
<TableColumn title="操作" :width="110">
|
||||
<template #default="{ record, index }">
|
||||
<Button
|
||||
type="link"
|
||||
@click="openVariableForm('out', record, index)"
|
||||
size="small"
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Divider type="vertical" />
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
danger
|
||||
@click="removeVariable('out', index)"
|
||||
>
|
||||
移除
|
||||
</Button>
|
||||
</template>
|
||||
</TableColumn>
|
||||
</Table>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
<!-- 添加或修改参数 -->
|
||||
<Modal
|
||||
v-model:open="variableDialogVisible"
|
||||
title="参数配置"
|
||||
:width="600"
|
||||
:destroy-on-close="true"
|
||||
@ok="saveVariable"
|
||||
@cancel="variableDialogVisible = false"
|
||||
>
|
||||
<Form
|
||||
:model="varialbeFormData"
|
||||
:label-col="{ span: 6 }"
|
||||
:wrapper-col="{ span: 18 }"
|
||||
ref="varialbeFormRef"
|
||||
>
|
||||
<FormItem label="源:" name="source">
|
||||
<Input v-model:value="varialbeFormData.source" allow-clear />
|
||||
</FormItem>
|
||||
<FormItem label="目标:" name="target">
|
||||
<Input v-model:value="varialbeFormData.target" allow-clear />
|
||||
</FormItem>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,46 +1,24 @@
|
||||
<!-- 表达式选择 -->
|
||||
<template>
|
||||
<Dialog title="请选择表达式" v-model="dialogVisible" width="1024px">
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
>
|
||||
<el-table-column label="名字" align="center" prop="name" />
|
||||
<el-table-column label="表达式" align="center" prop="expression" />
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="select(scope.row)">
|
||||
选择
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CommonStatusEnum } from '@/utils/constants';
|
||||
import {
|
||||
ProcessExpressionApi,
|
||||
ProcessExpressionVO,
|
||||
} from '@/api/bpm/processExpression';
|
||||
import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression';
|
||||
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
import { CommonStatusEnum } from '@vben/constants';
|
||||
|
||||
import { Button, Modal, Pagination, Table, TableColumn } from 'ant-design-vue';
|
||||
|
||||
import { getProcessExpressionPage } from '#/api/bpm/processExpression';
|
||||
import { ContentWrap } from '#/components/content-wrap';
|
||||
|
||||
/** BPM 流程 表单 */
|
||||
defineOptions({ name: 'ProcessExpressionDialog' });
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['select']);
|
||||
const dialogVisible = ref(false); // 弹窗的是否展示
|
||||
const loading = ref(true); // 列表的加载中
|
||||
const list = ref<ProcessExpressionVO[]>([]); // 列表的数据
|
||||
const list = ref<BpmProcessExpressionApi.ProcessExpression[]>([]); // 列表的数据
|
||||
const total = ref(0); // 列表的总页数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
@@ -62,8 +40,7 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const data =
|
||||
await ProcessExpressionApi.getProcessExpressionPage(queryParams);
|
||||
const data = await getProcessExpressionPage(queryParams);
|
||||
list.value = data.list;
|
||||
total.value = data.total;
|
||||
} finally {
|
||||
@@ -71,11 +48,49 @@ const getList = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']); // 定义 success 事件,用于操作成功后的回调
|
||||
const select = async (row) => {
|
||||
// 定义 select 事件,用于操作成功后的回调
|
||||
const select = async (row: BpmProcessExpressionApi.ProcessExpression) => {
|
||||
dialogVisible.value = false;
|
||||
// 发送操作成功的事件
|
||||
emit('select', row);
|
||||
};
|
||||
|
||||
// const handleCancel = () => {
|
||||
// dialogVisible.value = false;
|
||||
// };
|
||||
</script>
|
||||
<template>
|
||||
<Modal
|
||||
title="请选择表达式"
|
||||
v-model:open="dialogVisible"
|
||||
width="1024px"
|
||||
:footer="null"
|
||||
>
|
||||
<ContentWrap>
|
||||
<Table
|
||||
:loading="loading"
|
||||
:data-source="list"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
>
|
||||
<TableColumn title="名字" align="center" data-index="name" />
|
||||
<TableColumn title="表达式" align="center" data-index="expression" />
|
||||
<TableColumn title="操作" align="center">
|
||||
<template #default="{ record }">
|
||||
<Button type="primary" @click="select(record)"> 选择 </Button>
|
||||
</template>
|
||||
</TableColumn>
|
||||
</Table>
|
||||
<!-- 分页 -->
|
||||
<div class="mt-4 flex justify-end">
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:current="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
show-size-changer
|
||||
@change="getList"
|
||||
/>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -1,70 +1,38 @@
|
||||
<template>
|
||||
<div style="margin-top: 16px">
|
||||
<el-form-item label="消息实例">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<el-select v-model="bindMessageId" @change="updateTaskMessage">
|
||||
<el-option
|
||||
v-for="key in Object.keys(messageMap)"
|
||||
:value="key"
|
||||
:label="messageMap[key]"
|
||||
:key="key"
|
||||
/>
|
||||
</el-select>
|
||||
<XButton
|
||||
type="primary"
|
||||
preIcon="ep:plus"
|
||||
style="margin-left: 8px"
|
||||
@click="openMessageModel"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-dialog
|
||||
v-model="messageModelVisible"
|
||||
:close-on-click-modal="false"
|
||||
title="创建新消息"
|
||||
width="400px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form :model="newMessageForm" size="small" label-width="90px">
|
||||
<el-form-item label="消息ID">
|
||||
<el-input v-model="newMessageForm.id" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="消息名称">
|
||||
<el-input v-model="newMessageForm.name" clearable />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button size="small" type="primary" @click="createNewMessage"
|
||||
>确 认</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
h,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
toRaw,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { PlusOutlined } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Input,
|
||||
message,
|
||||
Modal,
|
||||
Select,
|
||||
SelectOption,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ReceiveTask' });
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: { type: String, default: '' },
|
||||
type: { type: String, default: '' },
|
||||
});
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
const bindMessageId = ref('');
|
||||
const newMessageForm = ref<any>({});
|
||||
const messageMap = ref<any>({});
|
||||
const newMessageForm = ref<Record<string, any>>({});
|
||||
const messageMap = ref<Record<string, any>>({});
|
||||
const messageModelVisible = ref(false);
|
||||
const bpmnElement = ref<any>();
|
||||
const bpmnMessageRefsMap = ref<any>();
|
||||
const bpmnMessageRefsMap = ref<Record<string, any>>();
|
||||
const bpmnRootElements = ref<any>();
|
||||
|
||||
const bpmnInstances = () => (window as any).bpmnInstances;
|
||||
@@ -88,17 +56,18 @@ const createNewMessage = () => {
|
||||
);
|
||||
bpmnRootElements.value.push(newMessage);
|
||||
messageMap.value[newMessageForm.value.id] = newMessageForm.value.name;
|
||||
bpmnMessageRefsMap.value[newMessageForm.value.id] = newMessage;
|
||||
// @ts-ignore
|
||||
bpmnMessageRefsMap.value?.[newMessageForm.value.id] = newMessage;
|
||||
messageModelVisible.value = false;
|
||||
};
|
||||
const updateTaskMessage = (messageId) => {
|
||||
const updateTaskMessage = (messageId: string) => {
|
||||
if (messageId === '-1') {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
messageRef: null,
|
||||
});
|
||||
} else {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
messageRef: bpmnMessageRefsMap.value[messageId],
|
||||
messageRef: bpmnMessageRefsMap.value?.[messageId],
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -108,9 +77,10 @@ onMounted(() => {
|
||||
bpmnRootElements.value =
|
||||
bpmnInstances().modeler.getDefinitions().rootElements;
|
||||
bpmnRootElements.value
|
||||
.filter((el) => el.$type === 'bpmn:Message')
|
||||
.forEach((m) => {
|
||||
bpmnMessageRefsMap.value[m.id] = m;
|
||||
.filter((el: any) => el.$type === 'bpmn:Message')
|
||||
.forEach((m: any) => {
|
||||
// @ts-ignore
|
||||
bpmnMessageRefsMap.value?.[m.id] = m;
|
||||
messageMap.value[m.id] = m.name;
|
||||
});
|
||||
messageMap.value['-1'] = '无';
|
||||
@@ -130,3 +100,57 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="margin-top: 16px">
|
||||
<Form.Item label="消息实例">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<Select
|
||||
v-model:value="bindMessageId"
|
||||
@change="(value: any) => updateTaskMessage(value)"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="key in Object.keys(messageMap)"
|
||||
:value="key"
|
||||
:label="messageMap[key]"
|
||||
:key="key"
|
||||
/>
|
||||
</Select>
|
||||
<Button
|
||||
type="primary"
|
||||
:icon="h(PlusOutlined)"
|
||||
style="margin-left: 8px"
|
||||
@click="openMessageModel"
|
||||
/>
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Modal
|
||||
v-model:open="messageModelVisible"
|
||||
:mask-closable="false"
|
||||
title="创建新消息"
|
||||
width="400px"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<Form :model="newMessageForm" size="small" :label-col="{ span: 6 }">
|
||||
<Form.Item label="消息ID">
|
||||
<Input v-model:value="newMessageForm.id" allow-clear />
|
||||
</Form.Item>
|
||||
<Form.Item label="消息名称">
|
||||
<Input v-model:value="newMessageForm.name" allow-clear />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<Button size="small" type="primary" @click="createNewMessage">
|
||||
确 认
|
||||
</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,57 +1,32 @@
|
||||
<template>
|
||||
<div style="margin-top: 16px">
|
||||
<el-form-item label="脚本格式">
|
||||
<el-input
|
||||
v-model="scriptTaskForm.scriptFormat"
|
||||
clearable
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="脚本类型">
|
||||
<el-select v-model="scriptTaskForm.scriptType">
|
||||
<el-option label="内联脚本" value="inline" />
|
||||
<el-option label="外部资源" value="external" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="脚本" v-show="scriptTaskForm.scriptType === 'inline'">
|
||||
<el-input
|
||||
v-model="scriptTaskForm.script"
|
||||
type="textarea"
|
||||
resize="vertical"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
clearable
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="资源地址"
|
||||
v-show="scriptTaskForm.scriptType === 'external'"
|
||||
>
|
||||
<el-input
|
||||
v-model="scriptTaskForm.resource"
|
||||
clearable
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结果变量">
|
||||
<el-input
|
||||
v-model="scriptTaskForm.resultVariable"
|
||||
clearable
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
defineOptions,
|
||||
defineProps,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
ref,
|
||||
toRaw,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import {
|
||||
FormItem,
|
||||
Input,
|
||||
Select,
|
||||
SelectOption,
|
||||
Textarea,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ScriptTask' });
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const defaultTaskForm = ref({
|
||||
scriptFormat: '',
|
||||
@@ -65,17 +40,19 @@ const bpmnElement = ref();
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
const resetTaskForm = () => {
|
||||
for (let key in defaultTaskForm.value) {
|
||||
let value =
|
||||
bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key];
|
||||
scriptTaskForm.value[key] = value;
|
||||
for (const key in defaultTaskForm.value) {
|
||||
// @ts-ignore
|
||||
scriptTaskForm.value[key] =
|
||||
bpmnElement.value?.businessObject[
|
||||
key as keyof typeof defaultTaskForm.value
|
||||
] || defaultTaskForm.value[key as keyof typeof defaultTaskForm.value];
|
||||
}
|
||||
scriptTaskForm.value.scriptType = scriptTaskForm.value.script
|
||||
? 'inline'
|
||||
: 'external';
|
||||
};
|
||||
const updateElementTask = () => {
|
||||
let taskAttr = Object.create(null);
|
||||
const taskAttr = Object.create(null);
|
||||
taskAttr.scriptFormat = scriptTaskForm.value.scriptFormat || null;
|
||||
taskAttr.resultVariable = scriptTaskForm.value.resultVariable || null;
|
||||
if (scriptTaskForm.value.scriptType === 'inline') {
|
||||
@@ -103,3 +80,50 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<FormItem label="脚本格式">
|
||||
<Input
|
||||
v-model:value="scriptTaskForm.scriptFormat"
|
||||
allow-clear
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="脚本类型">
|
||||
<Select v-model:value="scriptTaskForm.scriptType">
|
||||
<SelectOption value="inline">内联脚本</SelectOption>
|
||||
<SelectOption value="external">外部资源</SelectOption>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem label="脚本" v-show="scriptTaskForm.scriptType === 'inline'">
|
||||
<Textarea
|
||||
v-model:value="scriptTaskForm.script"
|
||||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||
allow-clear
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
label="资源地址"
|
||||
v-show="scriptTaskForm.scriptType === 'external'"
|
||||
>
|
||||
<Input
|
||||
v-model:value="scriptTaskForm.resource"
|
||||
allow-clear
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem label="结果变量">
|
||||
<Input
|
||||
v-model:value="scriptTaskForm.resultVariable"
|
||||
allow-clear
|
||||
@input="updateElementTask()"
|
||||
@change="updateElementTask()"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,56 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="执行类型" key="executeType">
|
||||
<el-select v-model="serviceTaskForm.executeType">
|
||||
<el-option label="Java类" value="class" />
|
||||
<el-option label="表达式" value="expression" />
|
||||
<el-option label="代理表达式" value="delegateExpression" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="serviceTaskForm.executeType === 'class'"
|
||||
label="Java类"
|
||||
prop="class"
|
||||
key="execute-class"
|
||||
>
|
||||
<el-input
|
||||
v-model="serviceTaskForm.class"
|
||||
clearable
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="serviceTaskForm.executeType === 'expression'"
|
||||
label="表达式"
|
||||
prop="expression"
|
||||
key="execute-expression"
|
||||
>
|
||||
<el-input
|
||||
v-model="serviceTaskForm.expression"
|
||||
clearable
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="serviceTaskForm.executeType === 'delegateExpression'"
|
||||
label="代理表达式"
|
||||
prop="delegateExpression"
|
||||
key="execute-delegate"
|
||||
>
|
||||
<el-input
|
||||
v-model="serviceTaskForm.delegateExpression"
|
||||
clearable
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import { FormItem, Input, Select } from 'ant-design-vue';
|
||||
|
||||
defineOptions({ name: 'ServiceTask' });
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: { type: String, default: '' },
|
||||
type: { type: String, default: '' },
|
||||
});
|
||||
|
||||
const defaultTaskForm = ref({
|
||||
@@ -66,8 +22,9 @@ const bpmnElement = ref();
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
|
||||
const resetTaskForm = () => {
|
||||
for (let key in defaultTaskForm.value) {
|
||||
let value =
|
||||
for (const key in defaultTaskForm.value) {
|
||||
const value =
|
||||
// @ts-ignore
|
||||
bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key];
|
||||
serviceTaskForm.value[key] = value;
|
||||
if (value) {
|
||||
@@ -77,9 +34,9 @@ const resetTaskForm = () => {
|
||||
};
|
||||
|
||||
const updateElementTask = () => {
|
||||
let taskAttr = Object.create(null);
|
||||
const taskAttr = Object.create(null);
|
||||
const type = serviceTaskForm.value.executeType;
|
||||
for (let key in serviceTaskForm.value) {
|
||||
for (const key in serviceTaskForm.value) {
|
||||
if (key !== 'executeType' && key !== type) taskAttr[key] = null;
|
||||
}
|
||||
taskAttr[type] = serviceTaskForm.value[type] || '';
|
||||
@@ -101,3 +58,54 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<FormItem label="执行类型" key="executeType">
|
||||
<Select
|
||||
v-model:value="serviceTaskForm.executeType"
|
||||
:options="[
|
||||
{ label: 'Java类', value: 'class' },
|
||||
{ label: '表达式', value: 'expression' },
|
||||
{ label: '代理表达式', value: 'delegateExpression' },
|
||||
]"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="serviceTaskForm.executeType === 'class'"
|
||||
label="Java类"
|
||||
name="class"
|
||||
key="execute-class"
|
||||
>
|
||||
<Input
|
||||
v-model:value="serviceTaskForm.class"
|
||||
allow-clear
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="serviceTaskForm.executeType === 'expression'"
|
||||
label="表达式"
|
||||
name="expression"
|
||||
key="execute-expression"
|
||||
>
|
||||
<Input
|
||||
v-model:value="serviceTaskForm.expression"
|
||||
allow-clear
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="serviceTaskForm.executeType === 'delegateExpression'"
|
||||
label="代理表达式"
|
||||
name="delegateExpression"
|
||||
key="execute-delegate"
|
||||
>
|
||||
<Input
|
||||
v-model:value="serviceTaskForm.delegateExpression"
|
||||
allow-clear
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</FormItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,250 +1,60 @@
|
||||
<template>
|
||||
<el-form label-width="120px">
|
||||
<el-form-item label="规则类型" prop="candidateStrategy">
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateStrategy"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="changeCandidateStrategy"
|
||||
>
|
||||
<el-option
|
||||
v-for="(dict, index) in CANDIDATE_STRATEGY"
|
||||
:key="index"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy == CandidateStrategy.ROLE"
|
||||
label="指定角色"
|
||||
prop="candidateParam"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
multiple
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
|
||||
userTaskForm.candidateStrategy == CandidateStrategy.DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ==
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
|
||||
"
|
||||
label="指定部门"
|
||||
prop="candidateParam"
|
||||
span="24"
|
||||
>
|
||||
<el-tree-select
|
||||
ref="treeRef"
|
||||
v-model="userTaskForm.candidateParam"
|
||||
:data="deptTreeOptions"
|
||||
:props="defaultProps"
|
||||
empty-text="加载中,请稍后"
|
||||
multiple
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy == CandidateStrategy.POST"
|
||||
label="指定岗位"
|
||||
prop="candidateParam"
|
||||
span="24"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
multiple
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy == CandidateStrategy.USER"
|
||||
label="指定用户"
|
||||
prop="candidateParam"
|
||||
span="24"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
multiple
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER_GROUP"
|
||||
label="指定用户组"
|
||||
prop="candidateParam"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
multiple
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.FORM_USER"
|
||||
label="表单内用户字段"
|
||||
prop="formUser"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="handleFormUserChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER
|
||||
"
|
||||
label="表单内部门字段"
|
||||
prop="formDept"
|
||||
>
|
||||
<el-select
|
||||
v-model="userTaskForm.candidateParam"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy ==
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ==
|
||||
CandidateStrategy.START_USER_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ==
|
||||
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
|
||||
"
|
||||
:label="deptLevelLabel!"
|
||||
prop="deptLevel"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="deptLevel" clearable @change="updateElementTask">
|
||||
<el-option
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.EXPRESSION"
|
||||
label="流程表达式"
|
||||
prop="candidateParam"
|
||||
>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="userTaskForm.candidateParam[0]"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
<XButton
|
||||
class="!w-1/1 mt-5px"
|
||||
type="success"
|
||||
preIcon="ep:select"
|
||||
title="选择表达式"
|
||||
size="small"
|
||||
@click="openProcessExpressionDialog"
|
||||
/>
|
||||
<!-- 选择弹窗 -->
|
||||
<ProcessExpressionDialog
|
||||
ref="processExpressionDialogRef"
|
||||
@select="selectProcessExpression"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="跳过表达式" prop="skipExpression">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="userTaskForm.skipExpression"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="updateSkipExpression"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { BpmProcessExpressionApi } from '#/api/bpm/processExpression';
|
||||
import type { BpmUserGroupApi } from '#/api/bpm/userGroup';
|
||||
import type { SystemPostApi } from '#/api/system/post';
|
||||
import type { SystemRoleApi } from '#/api/system/role';
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import {
|
||||
computed,
|
||||
h,
|
||||
inject,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
ref,
|
||||
toRaw,
|
||||
watch,
|
||||
} from 'vue';
|
||||
|
||||
import { SelectOutlined } from '@vben/icons';
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
FormItem,
|
||||
Select,
|
||||
SelectOption,
|
||||
Textarea,
|
||||
TreeSelect,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { getUserGroupSimpleList } from '#/api/bpm/userGroup';
|
||||
import { getSimpleDeptList } from '#/api/system/dept';
|
||||
import { getSimplePostList } from '#/api/system/post';
|
||||
import { getSimpleRoleList } from '#/api/system/role';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import {
|
||||
CANDIDATE_STRATEGY,
|
||||
CandidateStrategy,
|
||||
FieldPermissionType,
|
||||
MULTI_LEVEL_DEPT,
|
||||
} from '@/components/SimpleProcessDesignerV2/src/consts';
|
||||
import { defaultProps, handleTree } from '@/utils/tree';
|
||||
import * as RoleApi from '@/api/system/role';
|
||||
import * as DeptApi from '@/api/system/dept';
|
||||
import * as PostApi from '@/api/system/post';
|
||||
import * as UserApi from '@/api/system/user';
|
||||
import * as UserGroupApi from '@/api/bpm/userGroup';
|
||||
} from '#/components/simple-process-design/consts';
|
||||
import { useFormFieldsPermission } from '#/components/simple-process-design/helpers';
|
||||
|
||||
import ProcessExpressionDialog from './ProcessExpressionDialog.vue';
|
||||
import { ProcessExpressionVO } from '@/api/bpm/processExpression';
|
||||
import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node';
|
||||
|
||||
defineOptions({ name: 'UserTask' });
|
||||
const props = defineProps({
|
||||
id: String,
|
||||
type: String,
|
||||
id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const prefix = inject('prefix');
|
||||
const userTaskForm = ref({
|
||||
@@ -252,16 +62,24 @@ const userTaskForm = ref({
|
||||
candidateParam: [], // 分配选项
|
||||
skipExpression: '', // 跳过表达式
|
||||
});
|
||||
const bpmnElement = ref();
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances;
|
||||
const bpmnElement = ref<any>();
|
||||
const bpmnInstances = () => (window as Record<string, any>)?.bpmnInstances;
|
||||
|
||||
const roleOptions = ref<RoleApi.RoleVO[]>([]); // 角色列表
|
||||
const deptTreeOptions = ref(); // 部门树
|
||||
const postOptions = ref<PostApi.PostVO[]>([]); // 岗位列表
|
||||
const userOptions = ref<UserApi.UserVO[]>([]); // 用户列表
|
||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]); // 用户组列表
|
||||
const roleOptions = ref<SystemRoleApi.Role[]>([]); // 角色列表
|
||||
const deptTreeOptions = ref<any>(); // 部门树
|
||||
const postOptions = ref<SystemPostApi.Post[]>([]); // 岗位列表
|
||||
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
|
||||
const userGroupOptions = ref<BpmUserGroupApi.UserGroup[]>([]); // 用户组列表
|
||||
const treeRef = ref<any>();
|
||||
|
||||
const { formFieldOptions } = useFormFieldsPermission(FieldPermissionType.READ);
|
||||
|
||||
// 定义 TreeSelect 的默认属性映射
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
};
|
||||
// 表单内用户字段选项, 必须是必填和用户选择器
|
||||
const userFieldOnFormOptions = computed(() => {
|
||||
return formFieldOptions.filter((item) => item.type === 'UserSelect');
|
||||
@@ -275,21 +93,21 @@ const deptLevel = ref(1);
|
||||
const deptLevelLabel = computed(() => {
|
||||
let label = '部门负责人来源';
|
||||
if (
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
|
||||
) {
|
||||
label = label + '(指定部门向上)';
|
||||
label = `${label}(指定部门向上)`;
|
||||
} else if (
|
||||
userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
|
||||
userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER
|
||||
) {
|
||||
label = label + '(表单内部门向上)';
|
||||
label = `${label}(表单内部门向上)`;
|
||||
} else {
|
||||
label = label + '(发起人部门向上)';
|
||||
label = `${label}(发起人部门向上)`;
|
||||
}
|
||||
return label;
|
||||
});
|
||||
|
||||
const otherExtensions = ref();
|
||||
const otherExtensions = ref<any>();
|
||||
|
||||
const resetTaskForm = () => {
|
||||
const businessObject = bpmnElement.value.businessObject;
|
||||
@@ -301,50 +119,54 @@ const resetTaskForm = () => {
|
||||
businessObject?.extensionElements ??
|
||||
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] });
|
||||
userTaskForm.value.candidateStrategy = extensionElements.values?.filter(
|
||||
(ex) => ex.$type === `${prefix}:CandidateStrategy`,
|
||||
(ex: any) => ex.$type === `${prefix}:CandidateStrategy`,
|
||||
)?.[0]?.value;
|
||||
const candidateParamStr = extensionElements.values?.filter(
|
||||
(ex) => ex.$type === `${prefix}:CandidateParam`,
|
||||
(ex: any) => ex.$type === `${prefix}:CandidateParam`,
|
||||
)?.[0]?.value;
|
||||
if (candidateParamStr && candidateParamStr.length > 0) {
|
||||
// eslint-disable-next-line unicorn/prefer-switch
|
||||
if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) {
|
||||
// 特殊:流程表达式,只有一个 input 输入框
|
||||
// @ts-ignore
|
||||
userTaskForm.value.candidateParam = [candidateParamStr];
|
||||
} else if (
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
|
||||
) {
|
||||
// 特殊:多级不部门负责人,需要通过'|'分割
|
||||
userTaskForm.value.candidateParam = candidateParamStr
|
||||
.split('|')[0]
|
||||
.split(',')
|
||||
.map((item) => {
|
||||
.map((item: any) => {
|
||||
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
|
||||
let num = Number(item);
|
||||
const num = Number(item);
|
||||
return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER
|
||||
? item
|
||||
: num;
|
||||
});
|
||||
deptLevel.value = +candidateParamStr.split('|')[1];
|
||||
} else if (
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_DEPT_LEADER ||
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
|
||||
) {
|
||||
// @ts-ignore
|
||||
userTaskForm.value.candidateParam = +candidateParamStr;
|
||||
deptLevel.value = +candidateParamStr;
|
||||
} else if (
|
||||
userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.FORM_DEPT_LEADER
|
||||
) {
|
||||
userTaskForm.value.candidateParam = candidateParamStr.split('|')[0];
|
||||
deptLevel.value = +candidateParamStr.split('|')[1];
|
||||
} else {
|
||||
userTaskForm.value.candidateParam = candidateParamStr
|
||||
.split(',')
|
||||
.map((item) => {
|
||||
.map((item: any) => {
|
||||
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
|
||||
let num = Number(item);
|
||||
const num = Number(item);
|
||||
return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER
|
||||
? item
|
||||
: num;
|
||||
@@ -356,42 +178,41 @@ const resetTaskForm = () => {
|
||||
|
||||
otherExtensions.value =
|
||||
extensionElements.values?.filter(
|
||||
(ex) =>
|
||||
(ex: any) =>
|
||||
ex.$type !== `${prefix}:CandidateStrategy` &&
|
||||
ex.$type !== `${prefix}:CandidateParam`,
|
||||
) ?? [];
|
||||
|
||||
// 跳过表达式
|
||||
if (businessObject.skipExpression != undefined) {
|
||||
userTaskForm.value.skipExpression = businessObject.skipExpression;
|
||||
} else {
|
||||
userTaskForm.value.skipExpression = '';
|
||||
}
|
||||
userTaskForm.value.skipExpression =
|
||||
businessObject.skipExpression === undefined
|
||||
? ''
|
||||
: businessObject.skipExpression;
|
||||
|
||||
// 改用通过extensionElements来存储数据
|
||||
return;
|
||||
if (businessObject.candidateStrategy != undefined) {
|
||||
userTaskForm.value.candidateStrategy = parseInt(
|
||||
businessObject.candidateStrategy,
|
||||
) as any;
|
||||
} else {
|
||||
userTaskForm.value.candidateStrategy = undefined;
|
||||
}
|
||||
if (
|
||||
businessObject.candidateParam &&
|
||||
businessObject.candidateParam.length > 0
|
||||
) {
|
||||
if (userTaskForm.value.candidateStrategy === 60) {
|
||||
// 特殊:流程表达式,只有一个 input 输入框
|
||||
userTaskForm.value.candidateParam = [businessObject.candidateParam];
|
||||
} else {
|
||||
userTaskForm.value.candidateParam = businessObject.candidateParam
|
||||
.split(',')
|
||||
.map((item) => item);
|
||||
}
|
||||
} else {
|
||||
userTaskForm.value.candidateParam = [];
|
||||
}
|
||||
|
||||
// if (businessObject.candidateStrategy != undefined) {
|
||||
// userTaskForm.value.candidateStrategy = parseInt(
|
||||
// businessObject.candidateStrategy,
|
||||
// ) as any;
|
||||
// } else {
|
||||
// userTaskForm.value.candidateStrategy = undefined;
|
||||
// }
|
||||
// if (
|
||||
// businessObject.candidateParam &&
|
||||
// businessObject.candidateParam.length > 0
|
||||
// ) {
|
||||
// if (userTaskForm.value.candidateStrategy === 60) {
|
||||
// // 特殊:流程表达式,只有一个 input 输入框
|
||||
// userTaskForm.value.candidateParam = [businessObject.candidateParam];
|
||||
// } else {
|
||||
// userTaskForm.value.candidateParam = businessObject.candidateParam
|
||||
// .split(',')
|
||||
// .map((item) => item);
|
||||
// }
|
||||
// } else {
|
||||
// userTaskForm.value.candidateParam = [];
|
||||
// }
|
||||
};
|
||||
|
||||
/** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
|
||||
@@ -410,27 +231,26 @@ const changeCandidateStrategy = () => {
|
||||
|
||||
/** 选中某个 options 时候,更新 bpmn 图 */
|
||||
const updateElementTask = () => {
|
||||
let candidateParam =
|
||||
userTaskForm.value.candidateParam instanceof Array
|
||||
? userTaskForm.value.candidateParam.join(',')
|
||||
: userTaskForm.value.candidateParam;
|
||||
let candidateParam = Array.isArray(userTaskForm.value.candidateParam)
|
||||
? userTaskForm.value.candidateParam.join(',')
|
||||
: userTaskForm.value.candidateParam;
|
||||
|
||||
// 特殊处理多级部门情况
|
||||
if (
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
|
||||
userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
|
||||
userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER
|
||||
) {
|
||||
candidateParam += '|' + deptLevel.value;
|
||||
candidateParam += `|${deptLevel.value}`;
|
||||
}
|
||||
// 特殊处理发起人部门负责人、发起人连续部门负责人
|
||||
if (
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_DEPT_LEADER ||
|
||||
userTaskForm.value.candidateStrategy ==
|
||||
userTaskForm.value.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
|
||||
) {
|
||||
candidateParam = deptLevel.value + '';
|
||||
candidateParam = `${deptLevel.value}`;
|
||||
}
|
||||
|
||||
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||
@@ -449,11 +269,11 @@ const updateElementTask = () => {
|
||||
});
|
||||
|
||||
// 改用通过extensionElements来存储数据
|
||||
return;
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
candidateStrategy: userTaskForm.value.candidateStrategy,
|
||||
candidateParam: userTaskForm.value.candidateParam.join(','),
|
||||
});
|
||||
// return;
|
||||
// bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
// candidateStrategy: userTaskForm.value.candidateStrategy,
|
||||
// candidateParam: userTaskForm.value.candidateParam.join(','),
|
||||
// });
|
||||
};
|
||||
|
||||
const updateSkipExpression = () => {
|
||||
@@ -472,18 +292,22 @@ const updateSkipExpression = () => {
|
||||
};
|
||||
|
||||
// 打开监听器弹窗
|
||||
const processExpressionDialogRef = ref();
|
||||
const processExpressionDialogRef = ref<any>();
|
||||
const openProcessExpressionDialog = async () => {
|
||||
processExpressionDialogRef.value.open();
|
||||
};
|
||||
const selectProcessExpression = (expression: ProcessExpressionVO) => {
|
||||
const selectProcessExpression = (
|
||||
expression: BpmProcessExpressionApi.ProcessExpression,
|
||||
) => {
|
||||
// @ts-ignore
|
||||
userTaskForm.value.candidateParam = [expression.expression];
|
||||
updateElementTask();
|
||||
};
|
||||
|
||||
const handleFormUserChange = (e) => {
|
||||
const handleFormUserChange = (e: any) => {
|
||||
if (e === 'PROCESS_START_USER_ID') {
|
||||
userTaskForm.value.candidateParam = [];
|
||||
// @ts-ignore
|
||||
userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER;
|
||||
}
|
||||
updateElementTask();
|
||||
@@ -502,19 +326,238 @@ watch(
|
||||
|
||||
onMounted(async () => {
|
||||
// 获得角色列表
|
||||
roleOptions.value = await RoleApi.getSimpleRoleList();
|
||||
roleOptions.value = await getSimpleRoleList();
|
||||
// 获得部门列表
|
||||
const deptOptions = await DeptApi.getSimpleDeptList();
|
||||
const deptOptions = await getSimpleDeptList();
|
||||
deptTreeOptions.value = handleTree(deptOptions, 'id');
|
||||
// 获得岗位列表
|
||||
postOptions.value = await PostApi.getSimplePostList();
|
||||
postOptions.value = await getSimplePostList();
|
||||
// 获得用户列表
|
||||
userOptions.value = await UserApi.getSimpleUserList();
|
||||
userOptions.value = await getSimpleUserList();
|
||||
// 获得用户组列表
|
||||
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList();
|
||||
userGroupOptions.value = await getUserGroupSimpleList();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
bpmnElement.value = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
|
||||
<FormItem label="规则类型" name="candidateStrategy">
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateStrategy"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="changeCandidateStrategy"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(dict, index) in CANDIDATE_STRATEGY"
|
||||
:key="index"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.ROLE"
|
||||
label="指定角色"
|
||||
name="candidateParam"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy === CandidateStrategy.DEPT_MEMBER ||
|
||||
userTaskForm.candidateStrategy === CandidateStrategy.DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ===
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
|
||||
"
|
||||
label="指定部门"
|
||||
name="candidateParam"
|
||||
>
|
||||
<TreeSelect
|
||||
ref="treeRef"
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
:tree-data="deptTreeOptions"
|
||||
:field-names="defaultProps"
|
||||
placeholder="加载中,请稍后"
|
||||
multiple
|
||||
tree-checkable
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.POST"
|
||||
label="指定岗位"
|
||||
name="candidateParam"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER"
|
||||
label="指定用户"
|
||||
name="candidateParam"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.nickname"
|
||||
:value="item.id"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER_GROUP"
|
||||
label="指定用户组"
|
||||
name="candidateParam"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
mode="multiple"
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.FORM_USER"
|
||||
label="表单内用户字段"
|
||||
name="formUser"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="handleFormUserChange"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER
|
||||
"
|
||||
label="表单内部门字段"
|
||||
name="formDept"
|
||||
>
|
||||
<Select
|
||||
v-model:value="userTaskForm.candidateParam"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
>
|
||||
<SelectOption
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="
|
||||
userTaskForm.candidateStrategy ===
|
||||
CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy ===
|
||||
CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
|
||||
userTaskForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER
|
||||
"
|
||||
:label="deptLevelLabel!"
|
||||
name="deptLevel"
|
||||
>
|
||||
<Select v-model:value="deptLevel" allow-clear @change="updateElementTask">
|
||||
<SelectOption
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</Select>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
v-if="userTaskForm.candidateStrategy === CandidateStrategy.EXPRESSION"
|
||||
label="流程表达式"
|
||||
name="candidateParam"
|
||||
>
|
||||
<Textarea
|
||||
v-model:value="userTaskForm.candidateParam[0]"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="updateElementTask"
|
||||
/>
|
||||
<Button
|
||||
class="!w-1/1 mt-5px"
|
||||
type="primary"
|
||||
:icon="h(SelectOutlined)"
|
||||
@click="openProcessExpressionDialog"
|
||||
>
|
||||
选择表达式
|
||||
</Button>
|
||||
<!-- 选择弹窗 -->
|
||||
<ProcessExpressionDialog
|
||||
ref="processExpressionDialogRef"
|
||||
@select="selectProcessExpression"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem label="跳过表达式" name="skipExpression">
|
||||
<Textarea
|
||||
v-model:value="userTaskForm.skipExpression"
|
||||
allow-clear
|
||||
style="width: 100%"
|
||||
@change="updateSkipExpression"
|
||||
/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
@@ -1,223 +1,22 @@
|
||||
<template>
|
||||
<el-tabs v-model="tab">
|
||||
<el-tab-pane label="CRON表达式" name="cron">
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-input
|
||||
v-model="cronStr"
|
||||
readonly
|
||||
style="width: 400px; font-weight: bold"
|
||||
:key="'cronStr'"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 8px">
|
||||
<el-input
|
||||
v-model="fields.second"
|
||||
placeholder="秒"
|
||||
style="width: 80px"
|
||||
:key="'second'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.minute"
|
||||
placeholder="分"
|
||||
style="width: 80px"
|
||||
:key="'minute'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.hour"
|
||||
placeholder="时"
|
||||
style="width: 80px"
|
||||
:key="'hour'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.day"
|
||||
placeholder="天"
|
||||
style="width: 80px"
|
||||
:key="'day'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.month"
|
||||
placeholder="月"
|
||||
style="width: 80px"
|
||||
:key="'month'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.week"
|
||||
placeholder="周"
|
||||
style="width: 80px"
|
||||
:key="'week'"
|
||||
/>
|
||||
<el-input
|
||||
v-model="fields.year"
|
||||
placeholder="年"
|
||||
style="width: 80px"
|
||||
:key="'year'"
|
||||
/>
|
||||
</div>
|
||||
<el-tabs v-model="activeField" type="card" style="margin-bottom: 8px">
|
||||
<el-tab-pane
|
||||
v-for="f in cronFieldList"
|
||||
:label="f.label"
|
||||
:name="f.key"
|
||||
:key="f.key"
|
||||
>
|
||||
<div style="margin-bottom: 8px">
|
||||
<el-radio-group v-model="cronMode[f.key]" :key="'radio-' + f.key">
|
||||
<el-radio label="every" :key="'every-' + f.key"
|
||||
>每{{ f.label }}</el-radio
|
||||
>
|
||||
<el-radio label="range" :key="'range-' + f.key"
|
||||
>从
|
||||
<el-input-number
|
||||
v-model="cronRange[f.key][0]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="'range0-' + f.key"
|
||||
/>
|
||||
到
|
||||
<el-input-number
|
||||
v-model="cronRange[f.key][1]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="'range1-' + f.key"
|
||||
/>
|
||||
之间每{{ f.label }}</el-radio
|
||||
>
|
||||
<el-radio label="step" :key="'step-' + f.key"
|
||||
>从第
|
||||
<el-input-number
|
||||
v-model="cronStep[f.key][0]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="'step0-' + f.key"
|
||||
/>
|
||||
开始每
|
||||
<el-input-number
|
||||
v-model="cronStep[f.key][1]"
|
||||
:min="1"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="'step1-' + f.key"
|
||||
/>
|
||||
{{ f.label }}</el-radio
|
||||
>
|
||||
<el-radio label="appoint" :key="'appoint-' + f.key"
|
||||
>指定</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-if="cronMode[f.key] === 'appoint'">
|
||||
<el-checkbox-group
|
||||
v-model="cronAppoint[f.key]"
|
||||
:key="'group-' + f.key"
|
||||
>
|
||||
<el-checkbox
|
||||
v-for="n in f.max + 1"
|
||||
:label="pad(n - 1)"
|
||||
:key="'cb-' + f.key + '-' + (n - 1)"
|
||||
>{{ pad(n - 1) }}</el-checkbox
|
||||
>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="标准格式" name="iso" :key="'iso-tab'">
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-input
|
||||
v-model="isoStr"
|
||||
placeholder="如R1/2025-05-21T21:59:54/P3DT30M30S"
|
||||
style="width: 400px; font-weight: bold"
|
||||
:key="'isoStr'"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
循环次数:<el-input-number
|
||||
v-model="repeat"
|
||||
:min="1"
|
||||
style="width: 100px"
|
||||
:key="'repeat'"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
日期时间:<el-date-picker
|
||||
v-model="isoDate"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
style="width: 200px"
|
||||
:key="'isoDate'"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
当前时长:<el-input
|
||||
v-model="isoDuration"
|
||||
placeholder="如P3DT30M30S"
|
||||
style="width: 200px"
|
||||
:key="'isoDuration'"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
秒:<el-button
|
||||
v-for="s in [5, 10, 30, 50]"
|
||||
@click="setDuration('S', s)"
|
||||
:key="'sec-' + s"
|
||||
>{{ s }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
<div>
|
||||
分:<el-button
|
||||
v-for="m in [5, 10, 30, 50]"
|
||||
@click="setDuration('M', m)"
|
||||
:key="'min-' + m"
|
||||
>{{ m }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
<div>
|
||||
小时:<el-button
|
||||
v-for="h in [4, 8, 12, 24]"
|
||||
@click="setDuration('H', h)"
|
||||
:key="'hour-' + h"
|
||||
>{{ h }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
<div>
|
||||
天:<el-button
|
||||
v-for="d in [1, 2, 3, 4]"
|
||||
@click="setDuration('D', d)"
|
||||
:key="'day-' + d"
|
||||
>{{ d }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
<div>
|
||||
月:<el-button
|
||||
v-for="mo in [1, 2, 3, 4]"
|
||||
@click="setDuration('M', mo)"
|
||||
:key="'mon-' + mo"
|
||||
>{{ mo }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
<div>
|
||||
年:<el-button
|
||||
v-for="y in [1, 2, 3, 4]"
|
||||
@click="setDuration('Y', y)"
|
||||
:key="'year-' + y"
|
||||
>{{ y }}</el-button
|
||||
>自定义
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue';
|
||||
const props = defineProps({ value: String });
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
DatePicker,
|
||||
Input,
|
||||
InputNumber,
|
||||
Radio,
|
||||
Tabs,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const tab = ref('cron');
|
||||
@@ -279,14 +78,14 @@ const cronStep = ref({
|
||||
});
|
||||
|
||||
function pad(n) {
|
||||
return n < 10 ? '0' + n : '' + n;
|
||||
return n < 10 ? `0${n}` : `${n}`;
|
||||
}
|
||||
|
||||
watch(
|
||||
[fields, cronMode, cronAppoint, cronRange, cronStep],
|
||||
() => {
|
||||
// 组装cron表达式
|
||||
let arr = cronFieldList.map((f) => {
|
||||
const arr = cronFieldList.map((f) => {
|
||||
if (cronMode.value[f.key] === 'every') return '*';
|
||||
if (cronMode.value[f.key] === 'appoint')
|
||||
return cronAppoint.value[f.key].join(',') || '*';
|
||||
@@ -312,20 +111,23 @@ const isoDuration = ref('');
|
||||
function setDuration(type, val) {
|
||||
// 组装ISO 8601字符串
|
||||
let d = isoDuration.value;
|
||||
if (!d.includes(type)) d += val + type;
|
||||
else d = d.replace(new RegExp(`\\d+${type}`), val + type);
|
||||
if (d.includes(type)) {
|
||||
d = d.replace(new RegExp(`\\d+${type}`), val + type);
|
||||
} else {
|
||||
d += val + type;
|
||||
}
|
||||
isoDuration.value = d;
|
||||
updateIsoStr();
|
||||
}
|
||||
function updateIsoStr() {
|
||||
let str = `R${repeat.value}`;
|
||||
if (isoDate.value)
|
||||
str +=
|
||||
'/' +
|
||||
(typeof isoDate.value === 'string'
|
||||
str += `/${
|
||||
typeof isoDate.value === 'string'
|
||||
? isoDate.value
|
||||
: new Date(isoDate.value).toISOString());
|
||||
if (isoDuration.value) str += '/' + isoDuration.value;
|
||||
: new Date(isoDate.value).toISOString()
|
||||
}`;
|
||||
if (isoDuration.value) str += `/${isoDuration.value}`;
|
||||
isoStr.value = str;
|
||||
if (tab.value === 'iso') emit('change', isoStr.value);
|
||||
}
|
||||
@@ -340,3 +142,239 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<Tabs v-model:active-key="tab">
|
||||
<Tabs.TabPane key="cron" tab="CRON表达式">
|
||||
<div style="margin-bottom: 10px">
|
||||
<Input
|
||||
v-model:value="cronStr"
|
||||
readonly
|
||||
style="width: 400px; font-weight: bold"
|
||||
key="cronStr"
|
||||
/>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 8px">
|
||||
<Input
|
||||
v-model:value="fields.second"
|
||||
placeholder="秒"
|
||||
style="width: 80px"
|
||||
key="second"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.minute"
|
||||
placeholder="分"
|
||||
style="width: 80px"
|
||||
key="minute"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.hour"
|
||||
placeholder="时"
|
||||
style="width: 80px"
|
||||
key="hour"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.day"
|
||||
placeholder="天"
|
||||
style="width: 80px"
|
||||
key="day"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.month"
|
||||
placeholder="月"
|
||||
style="width: 80px"
|
||||
key="month"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.week"
|
||||
placeholder="周"
|
||||
style="width: 80px"
|
||||
key="week"
|
||||
/>
|
||||
<Input
|
||||
v-model:value="fields.year"
|
||||
placeholder="年"
|
||||
style="width: 80px"
|
||||
key="year"
|
||||
/>
|
||||
</div>
|
||||
<Tabs
|
||||
v-model:active-key="activeField"
|
||||
type="card"
|
||||
style="margin-bottom: 8px"
|
||||
>
|
||||
<Tabs.TabPane v-for="f in cronFieldList" :key="f.key" :tab="f.label">
|
||||
<div style="margin-bottom: 8px">
|
||||
<Radio.Group
|
||||
v-model:value="cronMode[f.key]"
|
||||
:key="`radio-${f.key}`"
|
||||
>
|
||||
<Radio value="every" :key="`every-${f.key}`">
|
||||
每{{ f.label }}
|
||||
</Radio>
|
||||
<Radio value="range" :key="`range-${f.key}`">
|
||||
从
|
||||
<InputNumber
|
||||
v-model:value="cronRange[f.key][0]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="`range0-${f.key}`"
|
||||
/>
|
||||
到
|
||||
<InputNumber
|
||||
v-model:value="cronRange[f.key][1]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="`range1-${f.key}`"
|
||||
/>
|
||||
之间每{{ f.label }}
|
||||
</Radio>
|
||||
<Radio value="step" :key="`step-${f.key}`">
|
||||
从第
|
||||
<InputNumber
|
||||
v-model:value="cronStep[f.key][0]"
|
||||
:min="f.min"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="`step0-${f.key}`"
|
||||
/>
|
||||
开始每
|
||||
<InputNumber
|
||||
v-model:value="cronStep[f.key][1]"
|
||||
:min="1"
|
||||
:max="f.max"
|
||||
size="small"
|
||||
style="width: 60px"
|
||||
:key="`step1-${f.key}`"
|
||||
/>
|
||||
{{ f.label }}
|
||||
</Radio>
|
||||
<Radio value="appoint" :key="`appoint-${f.key}`"> 指定 </Radio>
|
||||
</Radio.Group>
|
||||
</div>
|
||||
<div v-if="cronMode[f.key] === 'appoint'">
|
||||
<Checkbox.Group
|
||||
v-model:value="cronAppoint[f.key]"
|
||||
:key="`group-${f.key}`"
|
||||
>
|
||||
<Checkbox
|
||||
v-for="n in f.max + 1"
|
||||
:key="`cb-${f.key}-${n - 1}`"
|
||||
:value="pad(n - 1)"
|
||||
>
|
||||
{{ pad(n - 1) }}
|
||||
</Checkbox>
|
||||
</Checkbox.Group>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane key="iso" title="标准格式" tab="iso-tab">
|
||||
<div style="margin-bottom: 10px">
|
||||
<Input
|
||||
v-model:value="isoStr"
|
||||
placeholder="如R1/2025-05-21T21:59:54/P3DT30M30S"
|
||||
style="width: 400px; font-weight: bold"
|
||||
key="isoStr"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
循环次数:<InputNumber
|
||||
v-model:value="repeat"
|
||||
:min="1"
|
||||
style="width: 100px"
|
||||
key="repeat"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
日期时间:<DatePicker
|
||||
v-model:value="isoDate"
|
||||
show-time
|
||||
placeholder="选择日期时间"
|
||||
style="width: 200px"
|
||||
key="isoDate"
|
||||
/>
|
||||
</div>
|
||||
<div style="margin-bottom: 10px">
|
||||
当前时长:<Input
|
||||
v-model:value="isoDuration"
|
||||
placeholder="如P3DT30M30S"
|
||||
style="width: 200px"
|
||||
key="isoDuration"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
秒:
|
||||
<Button
|
||||
v-for="s in [5, 10, 30, 50]"
|
||||
@click="setDuration('S', s)"
|
||||
:key="`sec-${s}`"
|
||||
>
|
||||
{{ s }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
<div>
|
||||
分:
|
||||
<Button
|
||||
v-for="m in [5, 10, 30, 50]"
|
||||
@click="setDuration('M', m)"
|
||||
:key="`min-${m}`"
|
||||
>
|
||||
{{ m }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
<div>
|
||||
小时:
|
||||
<Button
|
||||
v-for="h in [4, 8, 12, 24]"
|
||||
@click="setDuration('H', h)"
|
||||
:key="`hour-${h}`"
|
||||
>
|
||||
{{ h }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
<div>
|
||||
天:
|
||||
<Button
|
||||
v-for="d in [1, 2, 3, 4]"
|
||||
@click="setDuration('D', d)"
|
||||
:key="`day-${d}`"
|
||||
>
|
||||
{{ d }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
<div>
|
||||
月:
|
||||
<Button
|
||||
v-for="mo in [1, 2, 3, 4]"
|
||||
@click="setDuration('M', mo)"
|
||||
:key="`mon-${mo}`"
|
||||
>
|
||||
{{ mo }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
<div>
|
||||
年:
|
||||
<Button
|
||||
v-for="y in [1, 2, 3, 4]"
|
||||
@click="setDuration('Y', y)"
|
||||
:key="`year-${y}`"
|
||||
>
|
||||
{{ y }}
|
||||
</Button>
|
||||
自定义
|
||||
</div>
|
||||
</div>
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
</template>
|
||||
|
||||
@@ -1,33 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-bottom: 10px">
|
||||
当前选择:<el-input v-model="isoString" readonly style="width: 300px" />
|
||||
</div>
|
||||
<div v-for="unit in units" :key="unit.key" style="margin-bottom: 8px">
|
||||
<span>{{ unit.label }}:</span>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
v-for="val in unit.presets"
|
||||
:key="val"
|
||||
size="mini"
|
||||
@click="setUnit(unit.key, val)"
|
||||
>{{ val }}</el-button
|
||||
>
|
||||
<el-input
|
||||
v-model.number="custom[unit.key]"
|
||||
size="mini"
|
||||
style="width: 60px; margin-left: 8px"
|
||||
placeholder="自定义"
|
||||
@change="setUnit(unit.key, custom[unit.key])"
|
||||
/>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, computed } from 'vue';
|
||||
const props = defineProps({ value: String });
|
||||
import { ref, watch } from 'vue';
|
||||
|
||||
import { Button, Input } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
const units = [
|
||||
@@ -42,7 +23,7 @@ const custom = ref({ Y: '', M: '', D: '', H: '', m: '', S: '' });
|
||||
const isoString = ref('');
|
||||
|
||||
function setUnit(key, val) {
|
||||
if (!val || isNaN(val)) {
|
||||
if (!val || Number.isNaN(val)) {
|
||||
custom.value[key] = '';
|
||||
return;
|
||||
}
|
||||
@@ -52,13 +33,13 @@ function setUnit(key, val) {
|
||||
|
||||
function updateIsoString() {
|
||||
let str = 'P';
|
||||
if (custom.value.Y) str += custom.value.Y + 'Y';
|
||||
if (custom.value.M) str += custom.value.M + 'M';
|
||||
if (custom.value.D) str += custom.value.D + 'D';
|
||||
if (custom.value.Y) str += `${custom.value.Y}Y`;
|
||||
if (custom.value.M) str += `${custom.value.M}M`;
|
||||
if (custom.value.D) str += `${custom.value.D}D`;
|
||||
if (custom.value.H || custom.value.m || custom.value.S) str += 'T';
|
||||
if (custom.value.H) str += custom.value.H + 'H';
|
||||
if (custom.value.m) str += custom.value.m + 'M';
|
||||
if (custom.value.S) str += custom.value.S + 'S';
|
||||
if (custom.value.H) str += `${custom.value.H}H`;
|
||||
if (custom.value.m) str += `${custom.value.m}M`;
|
||||
if (custom.value.S) str += `${custom.value.S}S`;
|
||||
isoString.value = str === 'P' ? '' : str;
|
||||
emit('change', isoString.value);
|
||||
}
|
||||
@@ -84,3 +65,35 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div style="margin-bottom: 10px">
|
||||
当前选择:<Input
|
||||
v-model:value="isoString"
|
||||
readonly
|
||||
style="width: 300px"
|
||||
/>
|
||||
</div>
|
||||
<div v-for="unit in units" :key="unit.key" style="margin-bottom: 8px">
|
||||
<span>{{ unit.label }}:</span>
|
||||
<Button.Group>
|
||||
<Button
|
||||
v-for="val in unit.presets"
|
||||
:key="val"
|
||||
size="small"
|
||||
@click="setUnit(unit.key, val)"
|
||||
>
|
||||
{{ val }}
|
||||
</Button>
|
||||
<Input
|
||||
v-model:value="custom[unit.key]"
|
||||
size="small"
|
||||
style="width: 60px; margin-left: 8px"
|
||||
placeholder="自定义"
|
||||
@change="setUnit(unit.key, custom[unit.key])"
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,178 +1,51 @@
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<div style="margin-top: 10px">
|
||||
<span>类型:</span>
|
||||
<el-button-group>
|
||||
<el-button
|
||||
size="mini"
|
||||
:type="type === 'time' ? 'primary' : ''"
|
||||
@click="setType('time')"
|
||||
>时间</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
:type="type === 'duration' ? 'primary' : ''"
|
||||
@click="setType('duration')"
|
||||
>持续</el-button
|
||||
>
|
||||
<el-button
|
||||
size="mini"
|
||||
:type="type === 'cycle' ? 'primary' : ''"
|
||||
@click="setType('cycle')"
|
||||
>循环</el-button
|
||||
>
|
||||
</el-button-group>
|
||||
<el-icon v-if="valid" color="green" style="margin-left: 8px"
|
||||
><CircleCheckFilled
|
||||
/></el-icon>
|
||||
</div>
|
||||
<div style=" display: flex; align-items: center;margin-top: 10px">
|
||||
<span>条件:</span>
|
||||
<el-input
|
||||
v-model="condition"
|
||||
:placeholder="placeholder"
|
||||
style="width: calc(100% - 100px)"
|
||||
:readonly="type !== 'duration' && type !== 'cycle'"
|
||||
@focus="handleInputFocus"
|
||||
@blur="updateNode"
|
||||
>
|
||||
<template #suffix>
|
||||
<el-tooltip v-if="!valid" content="格式错误" placement="top">
|
||||
<el-icon color="orange"><WarningFilled /></el-icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="helpText" placement="top">
|
||||
<el-icon
|
||||
color="#409EFF"
|
||||
style="cursor: pointer"
|
||||
@click="showHelp = true"
|
||||
><QuestionFilled
|
||||
/></el-icon>
|
||||
</el-tooltip>
|
||||
<el-button
|
||||
v-if="type === 'time'"
|
||||
@click="showDatePicker = true"
|
||||
style="margin-left: 4px"
|
||||
circle
|
||||
size="small"
|
||||
>
|
||||
<Icon icon="ep:calendar" />
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="type === 'duration'"
|
||||
@click="showDurationDialog = true"
|
||||
style="margin-left: 4px"
|
||||
circle
|
||||
size="small"
|
||||
>
|
||||
<Icon icon="ep:timer" />
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="type === 'cycle'"
|
||||
@click="showCycleDialog = true"
|
||||
style="margin-left: 4px"
|
||||
circle
|
||||
size="small"
|
||||
>
|
||||
<Icon icon="ep:setting" />
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<!-- 时间选择器 -->
|
||||
<el-dialog
|
||||
v-model="showDatePicker"
|
||||
title="选择时间"
|
||||
width="400px"
|
||||
@close="showDatePicker = false"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="dateValue"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
style="width: 100%"
|
||||
@change="onDateChange"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="showDatePicker = false">取消</el-button>
|
||||
<el-button type="primary" @click="onDateConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 持续时长选择器 -->
|
||||
<el-dialog
|
||||
v-model="showDurationDialog"
|
||||
title="时间配置"
|
||||
width="600px"
|
||||
@close="showDurationDialog = false"
|
||||
>
|
||||
<DurationConfig :value="condition" @change="onDurationChange" />
|
||||
<template #footer>
|
||||
<el-button @click="showDurationDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="onDurationConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 循环配置器 -->
|
||||
<el-dialog
|
||||
v-model="showCycleDialog"
|
||||
title="时间配置"
|
||||
width="800px"
|
||||
@close="showCycleDialog = false"
|
||||
>
|
||||
<CycleConfig :value="condition" @change="onCycleChange" />
|
||||
<template #footer>
|
||||
<el-button @click="showCycleDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="onCycleConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 帮助说明 -->
|
||||
<el-dialog
|
||||
v-model="showHelp"
|
||||
title="格式说明"
|
||||
width="600px"
|
||||
@close="showHelp = false"
|
||||
>
|
||||
<div v-html="helpHtml"></div>
|
||||
<template #footer>
|
||||
<el-button @click="showHelp = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import {
|
||||
CircleCheckFilled,
|
||||
WarningFilled,
|
||||
QuestionFilled,
|
||||
} from '@element-plus/icons-vue';
|
||||
import DurationConfig from './DurationConfig.vue';
|
||||
import CycleConfig from './CycleConfig.vue';
|
||||
import { createListenerObject, updateElementExtensions } from '../../utils';
|
||||
const bpmnInstances = () => (window as any).bpmnInstances;
|
||||
const props = defineProps({ businessObject: Object });
|
||||
const type = ref('time');
|
||||
const condition = ref('');
|
||||
const valid = ref(true);
|
||||
const showDatePicker = ref(false);
|
||||
const showDurationDialog = ref(false);
|
||||
const showCycleDialog = ref(false);
|
||||
const showHelp = ref(false);
|
||||
const dateValue = ref(null);
|
||||
const bpmnElement = ref(null);
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
const placeholder = computed(() => {
|
||||
import { computed, nextTick, onMounted, ref, toRaw, watch } from 'vue';
|
||||
|
||||
import {
|
||||
CheckCircleFilled,
|
||||
ExclamationCircleFilled,
|
||||
IconifyIcon,
|
||||
QuestionCircleFilled,
|
||||
} from '@vben/icons';
|
||||
|
||||
import { Button, DatePicker, Input, Modal, Tooltip } from 'ant-design-vue';
|
||||
|
||||
import CycleConfig from './CycleConfig.vue';
|
||||
import DurationConfig from './DurationConfig.vue';
|
||||
|
||||
const props = defineProps({
|
||||
businessObject: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
const bpmnInstances = () => (window as any).bpmnInstances;
|
||||
const type: Ref<string> = ref('time');
|
||||
const condition: Ref<string> = ref('');
|
||||
const valid: Ref<boolean> = ref(true);
|
||||
const showDatePicker: Ref<boolean> = ref(false);
|
||||
const showDurationDialog: Ref<boolean> = ref(false);
|
||||
const showCycleDialog: Ref<boolean> = ref(false);
|
||||
const showHelp: Ref<boolean> = ref(false);
|
||||
const dateValue: Ref<Date | null> = ref(null);
|
||||
// const bpmnElement = ref(null);
|
||||
|
||||
const placeholder = computed<string>(() => {
|
||||
if (type.value === 'time') return '请输入时间';
|
||||
if (type.value === 'duration') return '请输入持续时长';
|
||||
if (type.value === 'cycle') return '请输入循环表达式';
|
||||
return '';
|
||||
});
|
||||
const helpText = computed(() => {
|
||||
const helpText = computed<string>(() => {
|
||||
if (type.value === 'time') return '选择具体时间';
|
||||
if (type.value === 'duration') return 'ISO 8601格式,如PT1H';
|
||||
if (type.value === 'cycle') return 'CRON表达式或ISO 8601周期';
|
||||
return '';
|
||||
});
|
||||
const helpHtml = computed(() => {
|
||||
const helpHtml = computed<string>(() => {
|
||||
if (type.value === 'duration') {
|
||||
return `指定定时器之前要等待多长时间。S表示秒,M表示分,D表示天;P表示时间段,T表示精确到时间的时间段。<br>
|
||||
时间格式依然为ISO 8601格式,一年两个月三天四小时五分六秒内,可以写成P1Y2M3DT4H5M6S。<br>
|
||||
@@ -185,7 +58,7 @@ const helpHtml = computed(() => {
|
||||
});
|
||||
|
||||
// 初始化和监听
|
||||
function syncFromBusinessObject() {
|
||||
function syncFromBusinessObject(): void {
|
||||
if (props.businessObject) {
|
||||
const timerDef = (props.businessObject.eventDefinitions || [])[0];
|
||||
if (timerDef) {
|
||||
@@ -205,7 +78,7 @@ function syncFromBusinessObject() {
|
||||
onMounted(syncFromBusinessObject);
|
||||
|
||||
// 切换类型
|
||||
function setType(t) {
|
||||
function setType(t: string) {
|
||||
type.value = t;
|
||||
condition.value = '';
|
||||
updateNode();
|
||||
@@ -217,24 +90,24 @@ watch([type, condition], () => {
|
||||
// updateNode() // 可以注释掉,避免频繁触发
|
||||
});
|
||||
|
||||
function validate() {
|
||||
function validate(): boolean {
|
||||
if (type.value === 'time') {
|
||||
return !!condition.value && !isNaN(Date.parse(condition.value));
|
||||
return !!condition.value && !Number.isNaN(Date.parse(condition.value));
|
||||
}
|
||||
if (type.value === 'duration') {
|
||||
return /^P.*$/.test(condition.value);
|
||||
}
|
||||
if (type.value === 'cycle') {
|
||||
return /^([0-9*\/?, ]+|R\d*\/P.*)$/.test(condition.value);
|
||||
return /^(?:[0-9*/?, ]+|R\d*\/P.*)$/.test(condition.value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 选择时间
|
||||
function onDateChange(val) {
|
||||
function onDateChange(val: any) {
|
||||
dateValue.value = val;
|
||||
}
|
||||
function onDateConfirm() {
|
||||
function onDateConfirm(): void {
|
||||
if (dateValue.value) {
|
||||
condition.value = new Date(dateValue.value).toISOString();
|
||||
showDatePicker.value = false;
|
||||
@@ -243,35 +116,35 @@ function onDateConfirm() {
|
||||
}
|
||||
|
||||
// 持续时长
|
||||
function onDurationChange(val) {
|
||||
function onDurationChange(val: string) {
|
||||
condition.value = val;
|
||||
}
|
||||
function onDurationConfirm() {
|
||||
function onDurationConfirm(): void {
|
||||
showDurationDialog.value = false;
|
||||
updateNode();
|
||||
}
|
||||
|
||||
// 循环
|
||||
function onCycleChange(val) {
|
||||
function onCycleChange(val: string) {
|
||||
condition.value = val;
|
||||
}
|
||||
function onCycleConfirm() {
|
||||
function onCycleConfirm(): void {
|
||||
showCycleDialog.value = false;
|
||||
updateNode();
|
||||
}
|
||||
|
||||
// 输入框聚焦时弹窗(可选)
|
||||
function handleInputFocus() {
|
||||
function handleInputFocus(): void {
|
||||
if (type.value === 'time') showDatePicker.value = true;
|
||||
if (type.value === 'duration') showDurationDialog.value = true;
|
||||
if (type.value === 'cycle') showCycleDialog.value = true;
|
||||
}
|
||||
|
||||
// 同步到节点
|
||||
function updateNode() {
|
||||
const moddle = window.bpmnInstances?.moddle;
|
||||
const modeling = window.bpmnInstances?.modeling;
|
||||
const elementRegistry = window.bpmnInstances?.elementRegistry;
|
||||
function updateNode(): void {
|
||||
const moddle = (window.bpmnInstances as any)?.moddle;
|
||||
const modeling = (window.bpmnInstances as any)?.modeling;
|
||||
const elementRegistry = (window.bpmnInstances as any)?.elementRegistry;
|
||||
if (!moddle || !modeling || !elementRegistry) return;
|
||||
|
||||
// 获取元素
|
||||
@@ -340,6 +213,145 @@ watch(
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<div style="margin-top: 10px">
|
||||
<span>类型:</span>
|
||||
<Button.Group>
|
||||
<Button
|
||||
size="small"
|
||||
:type="type === 'time' ? 'primary' : 'default'"
|
||||
@click="setType('time')"
|
||||
>
|
||||
时间
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
:type="type === 'duration' ? 'primary' : 'default'"
|
||||
@click="setType('duration')"
|
||||
>
|
||||
持续
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
:type="type === 'cycle' ? 'primary' : 'default'"
|
||||
@click="setType('cycle')"
|
||||
>
|
||||
循环
|
||||
</Button>
|
||||
</Button.Group>
|
||||
<CheckCircleFilled v-if="valid" style="color: green; margin-left: 8px" />
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; margin-top: 10px">
|
||||
<span>条件:</span>
|
||||
<Input
|
||||
v-model:value="condition"
|
||||
:placeholder="placeholder"
|
||||
style="width: calc(100% - 100px)"
|
||||
:readonly="type !== 'duration' && type !== 'cycle'"
|
||||
@focus="handleInputFocus"
|
||||
@blur="updateNode"
|
||||
>
|
||||
<template #suffix>
|
||||
<Tooltip v-if="!valid" title="格式错误" placement="top">
|
||||
<ExclamationCircleFilled style="color: orange" />
|
||||
</Tooltip>
|
||||
<Tooltip :title="helpText" placement="top">
|
||||
<QuestionCircleFilled
|
||||
style="color: #409eff; cursor: pointer"
|
||||
@click="showHelp = true"
|
||||
/>
|
||||
</Tooltip>
|
||||
<Button
|
||||
v-if="type === 'time'"
|
||||
@click="showDatePicker = true"
|
||||
style="margin-left: 4px"
|
||||
shape="circle"
|
||||
size="small"
|
||||
>
|
||||
<IconifyIcon icon="ep:calendar" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="type === 'duration'"
|
||||
@click="showDurationDialog = true"
|
||||
style="margin-left: 4px"
|
||||
shape="circle"
|
||||
size="small"
|
||||
>
|
||||
<IconifyIcon icon="ep:timer" />
|
||||
</Button>
|
||||
<Button
|
||||
v-if="type === 'cycle'"
|
||||
@click="showCycleDialog = true"
|
||||
style="margin-left: 4px"
|
||||
shape="circle"
|
||||
size="small"
|
||||
>
|
||||
<IconifyIcon icon="ep:setting" />
|
||||
</Button>
|
||||
</template>
|
||||
</Input>
|
||||
</div>
|
||||
<!-- 时间选择器 -->
|
||||
<Modal
|
||||
v-model:open="showDatePicker"
|
||||
title="选择时间"
|
||||
width="400px"
|
||||
@cancel="showDatePicker = false"
|
||||
>
|
||||
<DatePicker
|
||||
v-model:value="dateValue"
|
||||
show-time
|
||||
placeholder="选择日期时间"
|
||||
style="width: 100%"
|
||||
@change="onDateChange"
|
||||
/>
|
||||
<template #footer>
|
||||
<Button @click="showDatePicker = false">取消</Button>
|
||||
<Button type="primary" @click="onDateConfirm">确定</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<!-- 持续时长选择器 -->
|
||||
<Modal
|
||||
v-model:open="showDurationDialog"
|
||||
title="时间配置"
|
||||
width="600px"
|
||||
@cancel="showDurationDialog = false"
|
||||
>
|
||||
<DurationConfig :value="condition" @change="onDurationChange" />
|
||||
<template #footer>
|
||||
<Button @click="showDurationDialog = false">取消</Button>
|
||||
<Button type="primary" @click="onDurationConfirm">确定</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<!-- 循环配置器 -->
|
||||
<Modal
|
||||
v-model:open="showCycleDialog"
|
||||
title="时间配置"
|
||||
width="800px"
|
||||
@cancel="showCycleDialog = false"
|
||||
>
|
||||
<CycleConfig :value="condition" @change="onCycleChange" />
|
||||
<template #footer>
|
||||
<Button @click="showCycleDialog = false">取消</Button>
|
||||
<Button type="primary" @click="onCycleConfirm">确定</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
<!-- 帮助说明 -->
|
||||
<Modal
|
||||
v-model:open="showHelp"
|
||||
title="格式说明"
|
||||
width="600px"
|
||||
@cancel="showHelp = false"
|
||||
>
|
||||
<div v-html="helpHtml"></div>
|
||||
<template #footer>
|
||||
<Button @click="showHelp = false">关闭</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 相关样式 */
|
||||
</style>
|
||||
|
||||
@@ -61,3 +61,15 @@ export const MenuOutlined = createIconifyIcon('ant-design:menu-outlined');
|
||||
export const PlusOutlined = createIconifyIcon('ant-design:plus-outlined');
|
||||
|
||||
export const SelectOutlined = createIconifyIcon('ant-design:select-outlined');
|
||||
|
||||
export const CheckCircleFilled = createIconifyIcon(
|
||||
'ant-design:check-circle-filled',
|
||||
);
|
||||
|
||||
export const ExclamationCircleFilled = createIconifyIcon(
|
||||
'ant-design:exclamation-circle-filled',
|
||||
);
|
||||
|
||||
export const QuestionCircleFilled = createIconifyIcon(
|
||||
'ant-design:question-circle-filled',
|
||||
);
|
||||
|
||||
4954
pnpm-lock.yaml
generated
4954
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user