初始版本提交
This commit is contained in:
129
src/views/workflow/design/bpmn/components/ApproveRules.vue
Normal file
129
src/views/workflow/design/bpmn/components/ApproveRules.vue
Normal file
@ -0,0 +1,129 @@
|
||||
<template>
|
||||
<FormItem :label="t('自动同意:')">
|
||||
<a-tree-select
|
||||
:value="props.autoAgreeRule"
|
||||
style="width: 100%"
|
||||
:tree-data="autoAgreeRuleOptions"
|
||||
tree-checkable
|
||||
allow-clear
|
||||
:placeholder="t('请选择自动同意规则')"
|
||||
@change="changeAutoAgreeRule"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem :label="t('无处理人:')">
|
||||
<a-select
|
||||
:value="props.noHandler"
|
||||
style="width: 100%"
|
||||
:options="noHandlerOptions"
|
||||
@change="changeNoHandler"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem :label="t('指定审批人:')">
|
||||
<a-select
|
||||
:value="props.isPrevChooseNext"
|
||||
style="width: 100%"
|
||||
:options="designatedApproverOptions"
|
||||
@change="changeDesignatedApprover"
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem
|
||||
:tip="
|
||||
t(
|
||||
'临时审批人是指由上一节点审批人指定下一节点审批人过程中,是否允许在审批人基础上添加组织架构人员。',
|
||||
)
|
||||
"
|
||||
:label="t('临时审批人:')"
|
||||
v-if="props.provisionalApprover == true || props.provisionalApprover == false"
|
||||
>
|
||||
<a-switch :checked="props.provisionalApprover" @change="changeProvisionalApprover" />
|
||||
</FormItem>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ProcessBasic">
|
||||
import FormItem from '/@bpmn/layout/FormItem.vue';
|
||||
import { AutoAgreeRule, DesignatedApprover, NoHandler } from '/@/enums/workflowEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits([
|
||||
'update:autoAgreeRule',
|
||||
'update:noHandler',
|
||||
'update:isPrevChooseNext',
|
||||
'update:provisionalApprover',
|
||||
]);
|
||||
const props = defineProps({
|
||||
autoAgreeRule: Array,
|
||||
noHandler: Number || String || Boolean,
|
||||
isPrevChooseNext: Number || String || Boolean,
|
||||
provisionalApprover: {
|
||||
type: Boolean || undefined,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
// 自动同意规则
|
||||
const autoAgreeRuleOptions = [
|
||||
{
|
||||
value: AutoAgreeRule.ORIGINATOR,
|
||||
label: t('候选审批人包含流程任务发起人'),
|
||||
},
|
||||
{
|
||||
value: AutoAgreeRule.PREVIOUS_NODE,
|
||||
label: t('候选审批人包含上一节点审批人'),
|
||||
},
|
||||
{
|
||||
value: AutoAgreeRule.APPROVED,
|
||||
label: t('候选审批人在此流程中审批过'),
|
||||
},
|
||||
];
|
||||
function changeAutoAgreeRule(val: Array<AutoAgreeRule>) {
|
||||
let autoAgreeRule = val;
|
||||
if (val.length > 0) {
|
||||
// 选择了自动同意规则后,无处理人 只能由 超级管理员处理 且 指定审批人 只能 不指定审批人
|
||||
emits('update:noHandler', NoHandler.ADMIN);
|
||||
emits('update:isPrevChooseNext', DesignatedApprover.NOT_SPECIFIED);
|
||||
}
|
||||
emits('update:autoAgreeRule', autoAgreeRule);
|
||||
}
|
||||
// 无处理人
|
||||
const noHandlerOptions = [
|
||||
{
|
||||
value: NoHandler.ADMIN,
|
||||
label: t('由超级管理员处理'),
|
||||
},
|
||||
{
|
||||
value: NoHandler.PREVIOUS_NODE,
|
||||
label: t('由上一节点审批人指定审批人'),
|
||||
},
|
||||
];
|
||||
function changeNoHandler(val: NoHandler) {
|
||||
emits('update:noHandler', val);
|
||||
if (val == NoHandler.PREVIOUS_NODE) {
|
||||
// 无处理人 选择了 由上一节点审批人指定审批人;那么自动同意规则必须为空
|
||||
emits('update:autoAgreeRule', []);
|
||||
}
|
||||
}
|
||||
// 指定审批人
|
||||
const designatedApproverOptions = [
|
||||
{
|
||||
value: DesignatedApprover.NOT_SPECIFIED,
|
||||
label: t('不指定审批人'),
|
||||
},
|
||||
{
|
||||
value: DesignatedApprover.PREVIOUS_NODE,
|
||||
label: t('由上一节点审批人指定'),
|
||||
},
|
||||
];
|
||||
function changeDesignatedApprover(val: DesignatedApprover) {
|
||||
emits('update:isPrevChooseNext', val);
|
||||
if (val == DesignatedApprover.PREVIOUS_NODE) {
|
||||
// 指定审批人 选择了 由上一节点审批人指定;那么自动同意规则必须为空
|
||||
emits('update:autoAgreeRule', []);
|
||||
}
|
||||
}
|
||||
// 临时审批人
|
||||
function changeProvisionalApprover(val: Boolean) {
|
||||
emits('update:provisionalApprover', val);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
298
src/views/workflow/design/bpmn/components/BasicPanel.vue
Normal file
298
src/views/workflow/design/bpmn/components/BasicPanel.vue
Normal file
@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div v-if="showPanel">
|
||||
<NodeHead class="mb-3" :nodeName="nodeName" />
|
||||
<a-tabs>
|
||||
<a-tab-pane key="1" :tab="t('基础配置')">
|
||||
<FormItem :required="formInfo.type !== 'bpmn:SequenceFlow'" :label="t('节点名称:')">
|
||||
<a-input
|
||||
v-model:value="formInfo.name"
|
||||
:placeholder="t('请填写节点名称')"
|
||||
@change="updateElementName"
|
||||
/>
|
||||
</FormItem>
|
||||
<slot name="basic"></slot>
|
||||
<FormItem :label="t('节点备注:')">
|
||||
<a-textarea v-model:value="formInfo.remark" :rows="8" />
|
||||
</FormItem>
|
||||
<slot name="noticePolicy"></slot>
|
||||
</a-tab-pane>
|
||||
<slot></slot>
|
||||
|
||||
<a-tab-pane key="99" :tab="t('事件监听')">
|
||||
<a-tabs>
|
||||
<a-tab-pane key="1" :tab="t('开始事件')">
|
||||
<div class="process-top">
|
||||
<a-button type="primary" @click="addStartEvent">
|
||||
{{ t('添加开始事件') }}
|
||||
</a-button>
|
||||
</div>
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="formInfo.startEventConfigs"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fangxiang1" />
|
||||
</svg>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
|
||||
<use xlink:href="#icon-paixu" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-if="column.key === 'type'">
|
||||
<a-select v-model:value="record[column.dataIndex]">
|
||||
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
|
||||
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-if="column.key === 'operateConfig'">
|
||||
<!-- <a-input
|
||||
v-if="record.type === NodeEventExType.API"
|
||||
v-model:value="record['apiConfig'].path"
|
||||
@click="showConfig(NodeEventType.START, index)"
|
||||
>
|
||||
<template #suffix>
|
||||
<Icon icon="ant-design:ellipsis-outlined" />
|
||||
</template>
|
||||
</a-input> -->
|
||||
<ScriptApiSelect
|
||||
v-if="record.type === NodeEventExType.API"
|
||||
style="width: 100%"
|
||||
v-model="record['apiConfig']"
|
||||
:need-hide-components="true"
|
||||
/>
|
||||
<a-select
|
||||
style="width: 100%"
|
||||
v-else-if="record.type === NodeEventExType.LITEFLOW"
|
||||
v-model:value="record['liteflowId']"
|
||||
:options="liteFlowOptions"
|
||||
:field-names="{ label: 'chainName', value: 'id' }"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteStartEvent(index)" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="t('结束事件')">
|
||||
<div class="process-top">
|
||||
<a-button type="primary" @click="addEndEvent"> {{ t('添加结束事件') }} </a-button>
|
||||
</div>
|
||||
<a-table :columns="columns" :data-source="formInfo.endEventConfigs" :pagination="false">
|
||||
<template #headerCell="{ column }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-fangxiang1" />
|
||||
</svg>
|
||||
</template>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'sort'">
|
||||
<svg class="icon draggable-icon" aria-hidden="true" style="cursor: move">
|
||||
<use xlink:href="#icon-paixu" />
|
||||
</svg>
|
||||
</template>
|
||||
<template v-if="column.key === 'type'">
|
||||
<a-select v-model:value="record[column.dataIndex]">
|
||||
<a-select-option :value="0">{{ t('执行API') }}</a-select-option>
|
||||
<a-select-option :value="1">{{ t('规则引擎') }}</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-if="column.key === 'operateConfig'">
|
||||
<ScriptApiSelect
|
||||
v-if="record.type === NodeEventExType.API"
|
||||
style="width: 100%"
|
||||
v-model="record['apiConfig']"
|
||||
:need-hide-components="true"
|
||||
/>
|
||||
<!-- <a-input
|
||||
v-if="record.type === NodeEventExType.API"
|
||||
v-model:value="record['apiConfig'].path"
|
||||
@click="showConfig(NodeEventType.END, index)"
|
||||
>
|
||||
<template #suffix>
|
||||
<Icon icon="ant-design:ellipsis-outlined" />
|
||||
</template>
|
||||
</a-input> -->
|
||||
<a-select
|
||||
style="width: 100%"
|
||||
v-else-if="record.type === NodeEventExType.LITEFLOW"
|
||||
v-model:value="record['liteflowId']"
|
||||
:options="liteFlowOptions"
|
||||
:field-names="{ label: 'chainName', value: 'id' }"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<DeleteTwoTone two-tone-color="#ff8080" @click="deleteEndEvent(index)" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
||||
<!-- <ApiConfig
|
||||
v-if="apiConfigDialog"
|
||||
:title="t('API配置')"
|
||||
v-model:apiConfigDialog="apiConfigDialog"
|
||||
v-model:apiConfig="
|
||||
formInfo[eventType === NodeEventType.START ? 'startEventConfigs' : 'endEventConfigs'][
|
||||
configIndex
|
||||
].apiConfig
|
||||
"
|
||||
/> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useStateFormInfo from '/@bpmn/hooks/useStateFormInfo';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import FormItem from '/@bpmn/layout/FormItem.vue';
|
||||
import { inject, nextTick, onMounted, ref, watch } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { DeleteTwoTone } from '@ant-design/icons-vue';
|
||||
import { getLiteflowList } from '/@/api/liteflow';
|
||||
// import { ApiConfig } from '/@/components/ApiConfig';
|
||||
import ScriptApiSelect from '/@bpmn/components/arguments/ScriptApiSelect.vue';
|
||||
import Sortable from 'sortablejs';
|
||||
import { NodeEventExType } from '/@/enums/workflowEnum';
|
||||
import { NodeEventConfig } from '/@/model/workflow/workflowConfig';
|
||||
const { t } = useI18n();
|
||||
const { showPanel, formInfo, nodeName } = useStateFormInfo();
|
||||
const updateElementName = inject('updateElementName') as any;
|
||||
|
||||
const liteFlowOptions = ref();
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
dataIndex: 'sort',
|
||||
key: 'sort',
|
||||
},
|
||||
{
|
||||
title: t('操作类别'),
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: '35%',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('操作配置'),
|
||||
dataIndex: 'operateConfig',
|
||||
key: 'operateConfig',
|
||||
width: '50%',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '25%',
|
||||
align: 'center',
|
||||
},
|
||||
]);
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
const addStartEvent = () => {
|
||||
formInfo.value.startEventConfigs.push({
|
||||
type: NodeEventExType.API,
|
||||
apiConfig: {},
|
||||
} as NodeEventConfig);
|
||||
};
|
||||
|
||||
const addEndEvent = () => {
|
||||
formInfo.value.endEventConfigs.push({
|
||||
type: NodeEventExType.API,
|
||||
apiConfig: {},
|
||||
} as NodeEventConfig);
|
||||
};
|
||||
|
||||
watch(
|
||||
() => formInfo.value,
|
||||
() => {
|
||||
if (
|
||||
formInfo.value.endEventConfigs?.length > 0 ||
|
||||
formInfo.value.startEventConfigs?.length > 0
|
||||
) {
|
||||
nextTick(() => {
|
||||
const tbody: any = document.querySelector('.ant-table-tbody');
|
||||
Sortable.create(tbody, {
|
||||
handle: '.draggable-icon',
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
const deleteStartEvent = (index) => {
|
||||
formInfo.value.startEventConfigs.splice(index, 1);
|
||||
};
|
||||
|
||||
const deleteEndEvent = (index) => {
|
||||
formInfo.value.endEventConfigs.splice(index, 1);
|
||||
};
|
||||
|
||||
const getList = async () => {
|
||||
liteFlowOptions.value = (await getLiteflowList()) || [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.node-box {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
box-shadow: -7px -1px 7px #dadcde;
|
||||
padding: 20px 30px 20px 20px;
|
||||
height: 100%;
|
||||
width: 410px;
|
||||
|
||||
.node-title {
|
||||
line-height: 20px;
|
||||
margin-bottom: 10px;
|
||||
padding-left: 6px;
|
||||
border-left: 6px solid #5e95ff;
|
||||
}
|
||||
|
||||
:deep(.ant-form-item) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.process-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.process-title {
|
||||
line-height: 18px;
|
||||
padding-left: 6px;
|
||||
border-left: 6px solid #5e95ff;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-select) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-table-cell) {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentcolor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
43
src/views/workflow/design/bpmn/components/CategoryTree.vue
Normal file
43
src/views/workflow/design/bpmn/components/CategoryTree.vue
Normal file
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="overflow-hidden">
|
||||
<NodeHead class="header-title" :node-name="t('流程分类')" />
|
||||
<BasicTree
|
||||
:clickRowToExpand="true"
|
||||
:treeData="treeData"
|
||||
:fieldNames="{ key: 'id', title: 'name' }"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDicDetailList } from '/@/api/system/dic';
|
||||
import { TreeItem } from '/@/components/Tree';
|
||||
import { BasicTree } from '/@/components/Tree';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { FlowCategory } from '/@/enums/workflowEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['select']);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
async function getList() {
|
||||
treeData.value = (await getDicDetailList({
|
||||
itemId: FlowCategory.ID,
|
||||
})) as unknown as TreeItem[];
|
||||
}
|
||||
function handleSelect(keys: string) {
|
||||
emits('select', keys[0]);
|
||||
}
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.header-title {
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
45
src/views/workflow/design/bpmn/components/InputModel.vue
Normal file
45
src/views/workflow/design/bpmn/components/InputModel.vue
Normal file
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div :class="hasSlot ? 'rule-box' : ''">
|
||||
<slot></slot>
|
||||
<template v-if="hasSlot">
|
||||
<span v-if="!value" class="rule-color">{{ placeholder }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Input class="rule-box" :value="value" :placeholder="placeholder" readonly>
|
||||
<template #suffix>
|
||||
<Icon icon="ant-design:ellipsis-outlined" class="rule-color" />
|
||||
</template>
|
||||
</Input>
|
||||
</template>
|
||||
<Icon icon="ant-design:ellipsis-outlined" class="rule-color" v-if="hasSlot" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { Input } from 'ant-design-vue';
|
||||
|
||||
defineProps({
|
||||
value: [String, Number],
|
||||
placeholder: String,
|
||||
});
|
||||
const hasSlot = computed(() => {
|
||||
return !!useSlots().default;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.rule-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
border: 1px solid #d9d9d9;
|
||||
padding: 0 4px;
|
||||
|
||||
.rule-color {
|
||||
color: #d0cfd0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<a-checkbox-group :value="props.modelValue" style="width: 100%" @change="change">
|
||||
<a-checkbox v-for="(item, index) in options" :key="index" :value="item.value">{{
|
||||
item.label
|
||||
}}</a-checkbox>
|
||||
</a-checkbox-group>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NoticePolicyType } from '/@/enums/workflowEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
const props = defineProps(['modelValue']);
|
||||
const options = [
|
||||
{ label: t('系统消息'), value: NoticePolicyType.SYSTEM_MESSAGES },
|
||||
{ label: t('短信'), value: NoticePolicyType.SHORT_MESSAGE },
|
||||
{ label: t('企业微信'), value: NoticePolicyType.ENTERPRISE_WECHAT },
|
||||
{ label: t('钉钉'), value: NoticePolicyType.DING_TALK },
|
||||
{ label: t('邮箱'), value: NoticePolicyType.MAILBOX },
|
||||
];
|
||||
function change(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div class="overflow-hidden">
|
||||
<NodeHead class="header-title" node-name="组织架构" />
|
||||
<BasicTree
|
||||
title=""
|
||||
:treeData="treeData"
|
||||
:fieldNames="{ key: 'id', title: 'name' }"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDepartmentTree } from '/@/api/system/department';
|
||||
import { TreeItem } from '/@/components/Tree';
|
||||
import { BasicTree } from '/@/components/Tree';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { onMounted, ref } from 'vue';
|
||||
const emits = defineEmits(['select']);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
async function getList() {
|
||||
treeData.value = (await getDepartmentTree()) as unknown as TreeItem[];
|
||||
}
|
||||
function handleSelect(keys: string) {
|
||||
emits('select', keys[0]);
|
||||
}
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.header-title {
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
217
src/views/workflow/design/bpmn/components/UpdateFormData.vue
Normal file
217
src/views/workflow/design/bpmn/components/UpdateFormData.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-button type="primary" class="bpmn-button" @click="show">{{ t('更新表单') }}</a-button>
|
||||
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="t('更新表单配置')"
|
||||
:forceRender="false"
|
||||
:closable="false"
|
||||
:width="1000"
|
||||
>
|
||||
<div class="box">
|
||||
<NodeHead class="box-head" :node-name="t('更新表单配置')" />
|
||||
<a-table :columns="columns" :data-source="formData.list" :pagination="false">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'status'">
|
||||
<a-spin v-if="!record.status" />
|
||||
<span v-else>{{ t('成功') }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'formType'">
|
||||
<span v-if="record.formType === 1">{{ t('自定义表单') }}</span>
|
||||
<span v-else>{{ t('系统表单') }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<a-button key="back" @click="submit">{{ t('取消') }}</a-button>
|
||||
<a-button key="submit" type="primary" :loading="loading" @click="submit">{{
|
||||
t('确定')
|
||||
}}</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { getFormConfig, compareFormInfo } from '/@bpmn/config/formPermission';
|
||||
import { BpmnNodeKey } from '/@/enums/workflowEnum';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { setProperties } from '/@bpmn/config/property';
|
||||
import {
|
||||
FormConfigItem,
|
||||
FormSettingItem,
|
||||
UpdateFormConfigItem,
|
||||
} from '/@/model/workflow/formSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const visible = ref(false);
|
||||
const formData: {
|
||||
list: Array<UpdateFormConfigItem>;
|
||||
indexMap: Map<string, number>;
|
||||
} = reactive({
|
||||
okDisabled: true,
|
||||
cancelDisabled: true,
|
||||
list: [],
|
||||
indexMap: new Map(),
|
||||
});
|
||||
const columns = [
|
||||
{
|
||||
title: t('序号'),
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
customRender: (column) => {
|
||||
return column.index + 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('表单名称'),
|
||||
dataIndex: 'formName',
|
||||
key: 'formName',
|
||||
},
|
||||
{
|
||||
title: t('表单ID'),
|
||||
dataIndex: 'formId',
|
||||
key: 'formId',
|
||||
},
|
||||
{
|
||||
title: t('表单类别'),
|
||||
key: 'formType',
|
||||
dataIndex: 'formType',
|
||||
},
|
||||
{
|
||||
title: t('所属节点'),
|
||||
key: 'nodeName',
|
||||
dataIndex: 'nodeName',
|
||||
},
|
||||
{
|
||||
title: t('更新状态'),
|
||||
key: 'status',
|
||||
},
|
||||
{
|
||||
title: t('备注'),
|
||||
key: 'remark',
|
||||
dataIndex: 'remark',
|
||||
},
|
||||
];
|
||||
const loading = computed(() => {
|
||||
if (
|
||||
formData.list.filter((ele) => {
|
||||
return ele.status;
|
||||
}).length == 0
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
function show() {
|
||||
visible.value = true;
|
||||
updateFormData();
|
||||
}
|
||||
function submit() {
|
||||
formData.list = [];
|
||||
formData.indexMap = new Map();
|
||||
visible.value = false;
|
||||
}
|
||||
async function updateFormData() {
|
||||
let formInfoData = new Map(); // formId 表单数据
|
||||
const store = useBpmnStore();
|
||||
const { info, defaultFormList, setDefaultFormList } = store;
|
||||
//更新默认表单
|
||||
if (defaultFormList.length > 0) {
|
||||
let newDefaultFormList: Array<FormSettingItem> = [];
|
||||
for (let index = 0; index < defaultFormList.length; index++) {
|
||||
const element = defaultFormList[index];
|
||||
let item = {
|
||||
key: element.key,
|
||||
formId: element.formId,
|
||||
formName: element.formName,
|
||||
formType: element.formType,
|
||||
};
|
||||
try {
|
||||
let result = await getFormConfig(item);
|
||||
newDefaultFormList.push(result);
|
||||
} catch (error) {}
|
||||
}
|
||||
setDefaultFormList(newDefaultFormList);
|
||||
}
|
||||
|
||||
for (const item of info.values()) {
|
||||
const showNodeTypes = [BpmnNodeKey.START, BpmnNodeKey.USER];
|
||||
if (showNodeTypes.includes(item.type)) {
|
||||
if (item.formConfigs) {
|
||||
item.formConfigs.forEach((ele, index) => {
|
||||
formInfoData.set(ele.formId, {
|
||||
key: ele.key,
|
||||
formType: ele.formType,
|
||||
formId: ele.formId,
|
||||
formName: ele.formName,
|
||||
});
|
||||
let formKey = item.id + ele.formId + index;
|
||||
let formKeyIndx = formData.list.length + 1;
|
||||
formData.indexMap.set(formKey, formKeyIndx);
|
||||
formData.list[formKeyIndx] = {
|
||||
formKey,
|
||||
formName: ele.formName,
|
||||
formId: ele.formId,
|
||||
formType: ele.formType,
|
||||
nodeName: item.name,
|
||||
status: false,
|
||||
remark: '',
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let [formId, value] of formInfoData) {
|
||||
let item = {
|
||||
formId: formId,
|
||||
key: value.key,
|
||||
formName: value.formName,
|
||||
formType: value.formType,
|
||||
};
|
||||
try {
|
||||
let result = await getFormConfig(item);
|
||||
formInfoData.set(formId, result);
|
||||
} catch (error) {}
|
||||
}
|
||||
for (const item of info.values()) {
|
||||
const showNodeTypes = [BpmnNodeKey.START, BpmnNodeKey.USER];
|
||||
let properties = item;
|
||||
if (showNodeTypes.includes(item.type)) {
|
||||
let formConfigs: Array<FormConfigItem> = [];
|
||||
if (item.formConfigs) {
|
||||
item.formConfigs.forEach((ele, index) => {
|
||||
let formKeyIndx = formData.indexMap.get(item.id + ele.formId + index);
|
||||
if (formInfoData.has(ele.formId)) {
|
||||
let formConfig: FormConfigItem = compareFormInfo(ele, formInfoData.get(ele.formId));
|
||||
formConfigs.push(formConfig);
|
||||
if (formKeyIndx) formData.list[formKeyIndx].status = true;
|
||||
} else {
|
||||
if (formKeyIndx) formData.list[formKeyIndx].status = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (formConfigs.length > 0) {
|
||||
properties.formConfigs = formConfigs;
|
||||
}
|
||||
setProperties(item.id, properties); // setInfo 合并新数据与旧数据
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.box {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.box-head {
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<SelectApiConfig
|
||||
v-model="data.config"
|
||||
:paramTree="ProcessDataVariable"
|
||||
:exampleStr="props.exampleStr"
|
||||
@update:modelValue="submit"
|
||||
>
|
||||
<InputModel
|
||||
:value="data.config.name"
|
||||
:placeholder="t('配置API')"
|
||||
style="width: 100%; min-width: 100px"
|
||||
/>
|
||||
</SelectApiConfig>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue';
|
||||
import { SelectApiConfig, InputModel } from '/@/components/ApiConfig';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ApiConfig } from '/@/components/ApiConfig/src/interface';
|
||||
import { ProcessDataVariable } from '/@bpmn/config/rules';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
let props = withDefaults(defineProps<{ modelValue: ApiConfig; exampleStr?: string }>(), {
|
||||
modelValue: () => {
|
||||
return {
|
||||
id: '',
|
||||
name: '',
|
||||
method: '',
|
||||
requestParamsConfigs: [], //Query Params
|
||||
requestHeaderConfigs: [], //Header
|
||||
requestBodyConfigs: [], //Body
|
||||
};
|
||||
},
|
||||
exampleStr: `{
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: 'value',
|
||||
}`,
|
||||
});
|
||||
let data: {
|
||||
config: ApiConfig;
|
||||
} = reactive({
|
||||
config: props.modelValue,
|
||||
});
|
||||
function submit() {
|
||||
emit('update:modelValue', cloneDeep(data.config));
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.editor-box {
|
||||
width: 300px;
|
||||
position: absolute;
|
||||
height: 350px;
|
||||
left: 140px;
|
||||
bottom: 13px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 0 6px 3px #ccc;
|
||||
|
||||
.editor-close {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: 13px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.editor-copy {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 15px;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
color: #5e95ff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<div @click.stop="open">
|
||||
<InputModel
|
||||
:value="data.config.name"
|
||||
:placeholder="t('配置API')"
|
||||
style="width: 100%; min-width: 100px"
|
||||
/>
|
||||
<ModalPanel
|
||||
:visible="visible"
|
||||
:width="1100"
|
||||
:title="t('API配置')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<NodeHead :nodeName="t('API信息')" class="title" />
|
||||
<SelectInterfaceAddress :config="data.config" @set-api-config="setApiConfig" />
|
||||
<ScriptInputParams
|
||||
class="padding"
|
||||
v-if="visible"
|
||||
:apiParams="data.apiParams"
|
||||
:paramTree="data.paramTree"
|
||||
:need-hide-components="needHideComponents"
|
||||
/>
|
||||
<a-button
|
||||
type="primary"
|
||||
class="btn-item"
|
||||
@click.stop="data.isShowExample = false"
|
||||
@mouseenter="data.isShowExample = true"
|
||||
>{{ t('返回出参示例') }}</a-button
|
||||
>
|
||||
<div class="editor-box" v-if="data.isShowExample">
|
||||
<CodeEditor
|
||||
v-if="data.isShowExample"
|
||||
:value="exampleStr"
|
||||
language="json"
|
||||
readonly
|
||||
style="font-size: 12px"
|
||||
/>
|
||||
<span class="editor-close" @click.stop="data.isShowExample = false"> x </span>
|
||||
<span class="editor-copy" @click.stop="copy">{{ t('复制代码') }}</span>
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { NodeHead, ModalPanel } from '/@/components/ModalPanel/index';
|
||||
import { SelectInterfaceAddress, InputModel } from '/@/components/ApiConfig';
|
||||
import { CodeEditor } from '/@/components/CodeEditor';
|
||||
import ScriptInputParams from './ScriptInputParams.vue';
|
||||
import { TreeProps } from 'ant-design-vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { InterfaceListInfo } from '/@/api/system/interface/model';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ApiConfig, ApiParams } from '/@/components/ApiConfig/src/interface';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
let props = withDefaults(
|
||||
defineProps<{
|
||||
paramTree?: TreeProps['treeData'];
|
||||
modelValue: ApiConfig;
|
||||
exampleStr?: string;
|
||||
needHideComponents?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
modelValue: () => {
|
||||
return {
|
||||
id: '',
|
||||
name: '',
|
||||
method: '',
|
||||
requestParamsConfigs: [], //Query Params
|
||||
requestHeaderConfigs: [], //Header
|
||||
requestBodyConfigs: [], //Body
|
||||
};
|
||||
},
|
||||
exampleStr: `{
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: 'value',
|
||||
}`,
|
||||
needHideComponents: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
);
|
||||
const { toClipboard } = useClipboard();
|
||||
let data: {
|
||||
isShowExample: boolean;
|
||||
config: ApiConfig;
|
||||
paramTree: TreeProps['treeData'];
|
||||
apiParams: Array<ApiParams>;
|
||||
} = reactive({
|
||||
isShowExample: false,
|
||||
config: props.modelValue,
|
||||
paramTree: [],
|
||||
apiParams: [
|
||||
{
|
||||
key: '1',
|
||||
title: 'Query Params',
|
||||
tableInfo: [],
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
title: 'Header',
|
||||
tableInfo: [],
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
title: 'Body',
|
||||
tableInfo: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const visible = ref<boolean>(false);
|
||||
function open() {
|
||||
data.config = cloneDeep(props.modelValue);
|
||||
if (props.paramTree) {
|
||||
data.paramTree = props.paramTree;
|
||||
}
|
||||
if (data.config.requestParamsConfigs)
|
||||
data.apiParams[0].tableInfo = data.config.requestParamsConfigs;
|
||||
if (data.config.requestHeaderConfigs)
|
||||
data.apiParams[1].tableInfo = data.config.requestHeaderConfigs;
|
||||
if (data.config.requestBodyConfigs)
|
||||
data.apiParams[2].tableInfo = data.config.requestBodyConfigs;
|
||||
visible.value = true;
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
async function copy() {
|
||||
try {
|
||||
await toClipboard(props.exampleStr);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function submit() {
|
||||
emit('update:modelValue', cloneDeep(data.config));
|
||||
close();
|
||||
}
|
||||
function setApiConfig(config: InterfaceListInfo) {
|
||||
if (data.config.id !== config.id) {
|
||||
data.config.id = config.id;
|
||||
data.config.name = config.name;
|
||||
data.config.method = config.method;
|
||||
data.config.path = config.path;
|
||||
data.config.requestParamsConfigs = [];
|
||||
data.config.requestHeaderConfigs = [];
|
||||
data.config.requestBodyConfigs = [];
|
||||
}
|
||||
if (data.config.requestParamsConfigs.length <= 0) {
|
||||
if (config.parameters) {
|
||||
config.parameters.forEach((element) => {
|
||||
data.config.requestParamsConfigs.push({
|
||||
name: element.name, //API入参名称
|
||||
dataType: element.dataType, //API入参类型
|
||||
assignmentType: 'value', //赋值类型
|
||||
value: element.value, //值
|
||||
config: '', //赋值配置
|
||||
});
|
||||
});
|
||||
}
|
||||
data.apiParams[0].tableInfo = data.config.requestParamsConfigs;
|
||||
}
|
||||
|
||||
if (data.config.requestHeaderConfigs.length <= 0) {
|
||||
if (config.headers) {
|
||||
config.headers.forEach((element) => {
|
||||
data.config.requestHeaderConfigs.push({
|
||||
name: element.name, //API入参名称
|
||||
dataType: element.dataType, //API入参类型
|
||||
assignmentType: 'value', //赋值类型
|
||||
value: element.value, //值
|
||||
config: '', //赋值配置
|
||||
});
|
||||
});
|
||||
}
|
||||
data.apiParams[1].tableInfo = data.config.requestHeaderConfigs;
|
||||
}
|
||||
|
||||
if (data.config.requestBodyConfigs.length <= 0) {
|
||||
if (config.requestBodyDefinition && config.requestBodyDefinition.children) {
|
||||
config.requestBodyDefinition.children.forEach((element) => {
|
||||
data.config.requestBodyConfigs.push({
|
||||
name: element.name, //API入参名称
|
||||
dataType: element.dataType, //API入参类型
|
||||
assignmentType: 'value', //赋值类型
|
||||
value: element.value, //值
|
||||
config: '', //赋值配置
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
data.apiParams[2].tableInfo = data.config.requestBodyConfigs;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.title {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.btn-item {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
}
|
||||
|
||||
.editor-box {
|
||||
width: 300px;
|
||||
position: absolute;
|
||||
height: 350px;
|
||||
left: 140px;
|
||||
bottom: 13px;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 0 6px 3px #ccc;
|
||||
|
||||
.editor-close {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
right: 13px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.editor-copy {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 15px;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
color: #5e95ff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<a-tabs>
|
||||
<a-tab-pane :key="item.key" :tab="item.title" v-for="item in apiParams">
|
||||
<a-table
|
||||
:dataSource="item.tableInfo"
|
||||
:columns="apiConfigColumns"
|
||||
:pagination="false"
|
||||
:scroll="{ y: '400px' }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'assignmentType'">
|
||||
<a-select
|
||||
v-model:value="record.assignmentType"
|
||||
style="width: 100%"
|
||||
:placeholder="t('请选择赋值类型')"
|
||||
@change="
|
||||
() => {
|
||||
record.config = null;
|
||||
}
|
||||
"
|
||||
allowClear
|
||||
>
|
||||
<a-select-option :value="bind.value" v-for="bind in bindType" :key="bind.value">
|
||||
{{ bind.label }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'value'">
|
||||
<!-- 流程参数 -->
|
||||
<a-select
|
||||
v-if="record.assignmentType === 'processParameter'"
|
||||
v-model:value="record.config"
|
||||
style="width: 100%"
|
||||
:placeholder="t('请选择流程参数')"
|
||||
allowClear
|
||||
>
|
||||
<a-select-option :value="it.id" v-for="it in data.processParameterTree" :key="it.id">
|
||||
{{ it.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
|
||||
<!-- 发起人信息 -->
|
||||
<a-tree-select
|
||||
v-else-if="record.assignmentType === 'originator'"
|
||||
v-model:value="record.config"
|
||||
show-search
|
||||
style="width: 100%"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:placeholder="t('请选择发起人信息')"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:tree-data="data.originatorTree"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
}"
|
||||
/>
|
||||
<!-- 表单数据 -->
|
||||
<a-tree-select
|
||||
v-else-if="record.assignmentType === 'formData'"
|
||||
v-model:value="record.config"
|
||||
show-search
|
||||
style="width: 100%"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
:placeholder="t('请选择表单数据')"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
:tree-data="data.formDataTree"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
}"
|
||||
/>
|
||||
<a-input
|
||||
v-else
|
||||
v-model:value="record.value"
|
||||
:placeholder="record.type ? t('请填写值') : t('请先选择赋值类型后再配置值')"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TreeProps } from 'ant-design-vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ApiParams } from '/@/components/ApiConfig/src/interface';
|
||||
import { reactive } from 'vue';
|
||||
import { getProcessParamConfigs, getVariablesTree } from '../../config/info';
|
||||
const { t } = useI18n();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
paramTree: TreeProps['treeData'];
|
||||
apiParams: Array<ApiParams>;
|
||||
needHideComponents?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
paramTree: () => {
|
||||
return [];
|
||||
},
|
||||
apiParams: () => {
|
||||
return [];
|
||||
},
|
||||
needHideComponents: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
processParameterTree: Array<{ id: string; name: string }>;
|
||||
originatorTree: TreeProps['treeData'];
|
||||
formDataTree: TreeProps['treeData'];
|
||||
} = reactive({
|
||||
processParameterTree: getProcessParamConfigs(),
|
||||
originatorTree: [
|
||||
{
|
||||
title: t('发起人ID'),
|
||||
key: '#{initiator_id}#',
|
||||
},
|
||||
{
|
||||
title: t('发起人所属组织架构名称'),
|
||||
key: '#{initiator_dept_name}#',
|
||||
},
|
||||
],
|
||||
formDataTree: getVariablesTree({ needHideComponents: props.needHideComponents as boolean }),
|
||||
});
|
||||
let bindType = [
|
||||
{
|
||||
label: t('值'),
|
||||
value: 'value',
|
||||
},
|
||||
{
|
||||
label: t('流程参数'),
|
||||
value: 'processParameter',
|
||||
},
|
||||
{
|
||||
label: t('发起人信息'),
|
||||
value: 'originator',
|
||||
},
|
||||
{
|
||||
label: t('表单数据'),
|
||||
value: 'formData',
|
||||
},
|
||||
];
|
||||
let apiConfigColumns = [
|
||||
{
|
||||
title: t('API入参名称'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('API入参类型'),
|
||||
dataIndex: 'dataType',
|
||||
key: 'dataType',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('赋值类型'),
|
||||
dataIndex: 'assignmentType',
|
||||
key: 'assignmentType',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('赋值配置'),
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
161
src/views/workflow/design/bpmn/components/card/FormCard.vue
Normal file
161
src/views/workflow/design/bpmn/components/card/FormCard.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="list-item">
|
||||
<div class="item-box">
|
||||
<div class="item-left"><img :src="FormImg" /></div>
|
||||
<div class="item-right">
|
||||
<div class="item-title">{{ t('功能名称') }}</div>
|
||||
<a-tooltip v-if="props.item.formName.length > 12" :title="props.item.formName">
|
||||
<div class="item-form-name"> {{ `${props.item.formName.slice(0, 12)}...` }}</div>
|
||||
</a-tooltip>
|
||||
<div class="item-form-name" v-else> {{ props.item.formName }}</div>
|
||||
</div>
|
||||
<div class="fixed-checked">
|
||||
<slot name="check"></slot>
|
||||
</div>
|
||||
<div class="fixed-circle"><PreviewForm :item="props.item" /></div>
|
||||
<div class="form-icon">
|
||||
<IconFontSymbol icon="form" :fill-color="fillColor" />
|
||||
</div>
|
||||
<div class="fixed-title"> {{ bgTitle }} </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import customFormImg from '/@/assets/workflow/custom-form.png';
|
||||
import systemFormImg from '/@/assets/workflow/system-form.png';
|
||||
import PreviewForm from '/@bpmn/components/formSettings/PreviewForm.vue';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { FormSettingItem } from '/@/model/workflow/formSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
defineEmits(['preview']);
|
||||
|
||||
let props = withDefaults(defineProps<{ item: FormSettingItem }>(), {
|
||||
item: () => {
|
||||
return { formName: '', key: '', formType: FormType.CUSTOM, formId: '' };
|
||||
},
|
||||
});
|
||||
|
||||
const FormImg = computed(() => {
|
||||
return props.item.formType == FormType.CUSTOM ? customFormImg : systemFormImg;
|
||||
});
|
||||
|
||||
const bgTitle = computed(() => {
|
||||
return props.item.formType == FormType.CUSTOM ? 'CUSTOM' : 'SYSTEM';
|
||||
});
|
||||
const fontColor = computed(() => {
|
||||
return props.item.formType == FormType.CUSTOM ? '#ab7efe' : '#18a1f8';
|
||||
});
|
||||
const fillColor = computed(() => {
|
||||
return props.item.formType == FormType.CUSTOM ? '#eceaff' : '#e8f6ff';
|
||||
});
|
||||
const bgColor = computed(() => {
|
||||
return props.item.formType == FormType.CUSTOM ? '#f4f3ff' : '#f4fafe';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-item {
|
||||
width: 30%;
|
||||
height: 100px;
|
||||
background: v-bind(bgColor);
|
||||
border-radius: 8px;
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
border-color: v-bind(fontColor);
|
||||
|
||||
&:hover {
|
||||
border: 1px dotted v-bind(fontColor);
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
margin: 14px;
|
||||
position: relative;
|
||||
|
||||
.item-left {
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
.item-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
margin: 10px 0 4px 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.item-form-name {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-circle {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-radius: 8px;
|
||||
border: 1px solid v-bind(fontColor);
|
||||
padding: 2px 10px;
|
||||
color: v-bind(fontColor);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.fixed-circle:hover {
|
||||
background-color: v-bind(fontColor);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.fixed-title {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 50px;
|
||||
color: #eaf1f5;
|
||||
font-size: 40px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.fixed-checked {
|
||||
position: absolute;
|
||||
bottom: -16px;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.form-icon {
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
bottom: -28px;
|
||||
font-size: 48px;
|
||||
transform: rotate(310deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-radius: 50%;
|
||||
border-color: v-bind(fontColor);
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
border-radius: 50%;
|
||||
background-color: v-bind(fontColor);
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked::after),
|
||||
:deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-inner),
|
||||
:deep(.ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
|
||||
border-color: v-bind(fontColor);
|
||||
}
|
||||
</style>
|
||||
143
src/views/workflow/design/bpmn/components/card/TemplateCard.vue
Normal file
143
src/views/workflow/design/bpmn/components/card/TemplateCard.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="list-item">
|
||||
<div class="item-box">
|
||||
<div class="item-left"><img :src="CardImg" /></div>
|
||||
<div class="item-right">
|
||||
<div class="template-box">
|
||||
<span class="item-title">{{ t('编码') }}</span>
|
||||
<span class="item-form-name">{{ props.item.code }}</span>
|
||||
</div>
|
||||
<div class="template-box">
|
||||
<span class="item-title">{{ t('名称') }}</span>
|
||||
<span class="item-form-name">{{ props.item.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-checked">
|
||||
<slot name="pick"></slot>
|
||||
</div>
|
||||
<div class="fixed-icon">
|
||||
<IconFontSymbol icon="liucheng" fill-color="#ece8ff" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import CardImg from '/@/assets/workflow/template.png';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
interface Obj {
|
||||
name: string;
|
||||
code: string;
|
||||
}
|
||||
let props = withDefaults(defineProps<{ item: Obj }>(), {
|
||||
item: () => {
|
||||
return { name: '', code: '' };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@custom-color: #5332f5;
|
||||
@bg-color: #f3f0ff;
|
||||
|
||||
.list-item {
|
||||
width: 31%;
|
||||
height: 92px;
|
||||
background: @bg-color;
|
||||
border-color: @custom-color;
|
||||
border-radius: 8px;
|
||||
margin: 1%;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
border: 3px solid transparent;
|
||||
|
||||
&:hover {
|
||||
border: 3px dotted @custom-color;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
margin: 14px;
|
||||
position: relative;
|
||||
height: 62px;
|
||||
align-items: center;
|
||||
|
||||
.item-left {
|
||||
margin-right: 14px;
|
||||
min-width: 70px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-right {
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
|
||||
.item-title {
|
||||
// font-weight: bold;
|
||||
color: #999999;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.item-form-name {
|
||||
width: calc(100% - 38px);
|
||||
margin-left: 4px;
|
||||
color: #999999;
|
||||
// font-weight: bold;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-checked {
|
||||
position: absolute;
|
||||
bottom: -6px;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.fixed-icon {
|
||||
position: absolute;
|
||||
right: -40px;
|
||||
font-size: 70px;
|
||||
transform: rotate(48deg);
|
||||
top: -40px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.template-box {
|
||||
display: flex;
|
||||
margin: 4px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked::after),
|
||||
:deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-inner),
|
||||
:deep(.ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click="show"> <slot></slot></div>
|
||||
<ModalPanel
|
||||
:visible="visible"
|
||||
:width="1200"
|
||||
:title="t('添加流程')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<template #left>
|
||||
<CategoryTree @select="handleSelect" />
|
||||
</template>
|
||||
<Selected :list="template.selected" @change="selectedListChecked" />
|
||||
<SearchBox @search="searchList" />
|
||||
|
||||
<div class="list-page-box" v-if="template.list.length > 0">
|
||||
<TemplateCard
|
||||
:class="selectIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
v-for="(item, index) in template.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #pick>
|
||||
<a-checkbox size="small" :checked="selectIds.includes(item.id)" />
|
||||
</template>
|
||||
</TemplateCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="page.current"
|
||||
:pageSize="page.pageSize"
|
||||
:total="page.total"
|
||||
show-less-items
|
||||
@change="getList"
|
||||
/></div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<EmptyBox />
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import CategoryTree from '/@bpmn/components/CategoryTree.vue';
|
||||
import TemplateCard from '/@bpmn/components/card/TemplateCard.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
import Selected from './Selected.vue';
|
||||
import { RelationProcessConfig } from '/@/model/workflow/workflowConfig';
|
||||
import { getDesignPage } from '/@/api/workflow/design';
|
||||
import { WorkflowPageModel } from '/@/api/workflow/model';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list: Array<RelationProcessConfig>;
|
||||
single: Boolean;
|
||||
}>(),
|
||||
{
|
||||
list: () => {
|
||||
return [];
|
||||
},
|
||||
single: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
);
|
||||
const visible = ref(false);
|
||||
|
||||
let category = ref('');
|
||||
|
||||
let template: {
|
||||
list: Array<WorkflowPageModel>;
|
||||
selected: Array<RelationProcessConfig>;
|
||||
keyword: string;
|
||||
} = reactive({ list: [], selected: [], keyword: '' });
|
||||
|
||||
let page: { current: number; total: number; pageSize: number } = reactive({
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 9,
|
||||
});
|
||||
|
||||
async function show() {
|
||||
page.current = 1;
|
||||
page.total = 0;
|
||||
category.value = '';
|
||||
template.selected = [];
|
||||
if (props.list.length > 0) {
|
||||
template.selected = cloneDeep(props.list).filter((ele) => {
|
||||
return ele.id;
|
||||
});
|
||||
}
|
||||
await getList();
|
||||
visible.value = true;
|
||||
}
|
||||
let selectIds = computed(() => {
|
||||
if (template.selected && template.selected.length > 0) {
|
||||
return template.selected.map((ele: RelationProcessConfig) => {
|
||||
return ele.id;
|
||||
});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
async function getList() {
|
||||
try {
|
||||
let params = {
|
||||
limit: page.current,
|
||||
size: page.pageSize,
|
||||
category: category.value,
|
||||
keyword: template.keyword,
|
||||
};
|
||||
let res = await getDesignPage(params);
|
||||
page.total = res.total;
|
||||
template.list = res.list;
|
||||
} catch (error) {}
|
||||
}
|
||||
function submit() {
|
||||
emits('change', template.selected);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
template.list = [];
|
||||
visible.value = false;
|
||||
}
|
||||
function handleSelect(deptId = '') {
|
||||
category.value = deptId;
|
||||
getList();
|
||||
}
|
||||
function checked(item: WorkflowPageModel) {
|
||||
if (props.single) {
|
||||
if (selectIds.value.includes(item.id)) {
|
||||
template.selected = [];
|
||||
} else {
|
||||
template.selected = [
|
||||
{
|
||||
name: item.name,
|
||||
id: item.id,
|
||||
code: item.code,
|
||||
definitionKey: item.definitionKey,
|
||||
},
|
||||
];
|
||||
}
|
||||
} else {
|
||||
if (selectIds.value.includes(item.id)) {
|
||||
template.selected = template.selected.filter((ele) => {
|
||||
return ele.id != item.id;
|
||||
});
|
||||
} else {
|
||||
template.selected.push({
|
||||
name: item.name,
|
||||
id: item.id,
|
||||
code: item.code,
|
||||
definitionKey: item.definitionKey,
|
||||
processStatus: 0, //流程状态
|
||||
processAuth: 0, //任务权限
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function selectedListChecked(item: RelationProcessConfig) {
|
||||
template.selected = template.selected.filter((ele) => {
|
||||
return ele.id != item.id;
|
||||
});
|
||||
}
|
||||
function searchList(searchKeywords: string) {
|
||||
template.keyword = searchKeywords;
|
||||
console.log('searchKeywords: ', searchKeywords);
|
||||
getList();
|
||||
}
|
||||
</script>
|
||||
140
src/views/workflow/design/bpmn/components/design/Selected.vue
Normal file
140
src/views/workflow/design/bpmn/components/design/Selected.vue
Normal file
@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="title">
|
||||
<NodeHead :node-name="t('流程模板列表')" />
|
||||
<a-button class="selected-btn" @click="open">{{ t('已选流程模板列表') }}</a-button>
|
||||
</div>
|
||||
<a-drawer
|
||||
placement="right"
|
||||
:visible="data.visible"
|
||||
:get-container="false"
|
||||
:closable="false"
|
||||
:mask="false"
|
||||
>
|
||||
<div class="selected-head title">
|
||||
<NodeHead :node-name="t('已选流程模板列表')" />
|
||||
<div class="close-icon" @click="close">+</div>
|
||||
</div>
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-box" v-if="data.list && data.list.length > 0">
|
||||
<TemplateCard
|
||||
class="picked"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="listChecked(item)"
|
||||
>
|
||||
<template #pick> <a-checkbox size="small" :checked="true" /> </template
|
||||
></TemplateCard>
|
||||
</div>
|
||||
<EmptyBox v-else />
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, watch } from 'vue';
|
||||
import { NodeHead, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
import TemplateCard from '/@bpmn/components/card/TemplateCard.vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { RelationProcessConfig } from '/@/model/workflow/workflowConfig';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
list: Array<RelationProcessConfig>;
|
||||
}>(),
|
||||
{
|
||||
list: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
let data: {
|
||||
visible: Boolean;
|
||||
list: Array<RelationProcessConfig>;
|
||||
keyword: string;
|
||||
} = reactive({
|
||||
visible: false,
|
||||
list: [],
|
||||
keyword: '',
|
||||
});
|
||||
|
||||
watch(props, (val) => {
|
||||
data.list = cloneDeep(val.list);
|
||||
});
|
||||
|
||||
function open() {
|
||||
data.list = cloneDeep(props.list);
|
||||
data.visible = true;
|
||||
}
|
||||
function close() {
|
||||
data.visible = false;
|
||||
}
|
||||
function search(keyword = '') {
|
||||
data.keyword = keyword;
|
||||
data.list = cloneDeep(
|
||||
props.list.filter((ele) => {
|
||||
return ele.name.includes(data.keyword);
|
||||
}),
|
||||
);
|
||||
}
|
||||
function listChecked(item) {
|
||||
emits('change', item);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-content-wrapper) {
|
||||
width: 100% !important;
|
||||
box-shadow: 0px 2px 2px 2px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-open) {
|
||||
position: absolute;
|
||||
width: calc(100% - 244px) !important;
|
||||
top: 50px;
|
||||
left: 240px;
|
||||
box-shadow: -5px 5px 4px 1px rgb(0 0 0 / 6%);
|
||||
height: calc(100% - 110px);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.selected-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.selected-btn {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 24px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 3px;
|
||||
border-style: dotted;
|
||||
border-color: #5332f5;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div class="form-category">
|
||||
<NodeHead class="title" :node-name="t('表单类型')" />
|
||||
<div class="tree-box">
|
||||
<div
|
||||
class="item"
|
||||
:class="props.modelValue == FormType.WORKFLOW ? 'checked' : ''"
|
||||
@click="change(FormType.WORKFLOW)"
|
||||
>
|
||||
<IconFontSymbol class="item-icon" icon="xitong" />
|
||||
<span class="item-text">{{ t('流程已添加表单') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="item"
|
||||
:class="props.modelValue == FormType.CUSTOM ? 'checked' : ''"
|
||||
@click="change(FormType.CUSTOM)"
|
||||
>
|
||||
<IconFontSymbol class="item-icon" icon="zidingyi" />
|
||||
<span class="item-text">{{ t('自定义表单') }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="item"
|
||||
:class="props.modelValue == FormType.SYSTEM ? 'checked' : ''"
|
||||
@click="change(FormType.SYSTEM)"
|
||||
>
|
||||
<IconFontSymbol class="item-icon" icon="xitong" />
|
||||
<span class="item-text">{{ t('系统表单') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
const props = defineProps(['modelValue']);
|
||||
function change(type: FormType) {
|
||||
emit('update:modelValue', type);
|
||||
emit('change');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form-category {
|
||||
.title {
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.tree-box {
|
||||
padding: 10px 0;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
padding: 10px;
|
||||
|
||||
.item-icon {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.item-text {
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.checked {
|
||||
background: #e5f1ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<SettingModal
|
||||
:list="[
|
||||
{
|
||||
formId: config.formId,
|
||||
formName: config.formName,
|
||||
formType: config.formType,
|
||||
key: config.key,
|
||||
},
|
||||
]"
|
||||
:isSingle="true"
|
||||
@submit="submit"
|
||||
>
|
||||
<InputModel :value="config.formName" :placeholder="t('请选择功能表单')" />
|
||||
</SettingModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SettingModal from '/@bpmn/components/formSettings/SettingModal.vue';
|
||||
import InputModel from '/@bpmn/components/InputModel.vue';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { FormSettingItem } from '/@/model/workflow/formSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let emits = defineEmits(['update:config']);
|
||||
withDefaults(defineProps<{ config: FormSettingItem }>(), {
|
||||
config: () => {
|
||||
return { formType: FormType.CUSTOM, formId: '', formName: '', key: '' };
|
||||
},
|
||||
});
|
||||
// FormSettingItem
|
||||
function submit(list: Array<FormSettingItem>) {
|
||||
let submitJson = {
|
||||
enabled: true,
|
||||
formId: '',
|
||||
formName: '',
|
||||
formType: FormType.CUSTOM,
|
||||
};
|
||||
if (list.length > 0) {
|
||||
submitJson.formId = list[0].formId;
|
||||
submitJson.formName = list[0].formName;
|
||||
submitJson.formType = list[0].formType;
|
||||
}
|
||||
emits('update:config', submitJson);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.slot-item) {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.open-icon {
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div @click.stop="open">{{ t('表单预览') }}</div>
|
||||
<a-modal v-model:visible="visible" :title="t('预览')" :width="1100" :footer="null">
|
||||
<PreviewSystemForm
|
||||
v-if="visible && props.item.formType == FormType.SYSTEM"
|
||||
:systemComponent="getFormProps.systemComponent"
|
||||
/>
|
||||
<SimpleForm
|
||||
v-else
|
||||
ref="formRef"
|
||||
class="form"
|
||||
:formProps="getFormProps"
|
||||
:formModel="formData"
|
||||
:isWorkFlow="true"
|
||||
/>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { getSchemasList } from '/@bpmn/config/formPermission';
|
||||
import { FormSchema } from '/@/components/Form';
|
||||
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
|
||||
import { FormSettingItem } from '/@/model/workflow/formSetting';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { PreviewSystemForm } from '/@/components/SystemForm/index';
|
||||
import { SystemComponentConfig } from '/@/model/workflow/bpmnConfig';
|
||||
const { t } = useI18n();
|
||||
let props = withDefaults(defineProps<{ item: FormSettingItem }>(), {
|
||||
item: () => {
|
||||
return { formType: FormType.CUSTOM, formId: '', key: '', formName: '' };
|
||||
},
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
let getFormProps: {
|
||||
schemas: FormSchema[];
|
||||
showResetButton: boolean;
|
||||
showSubmitButton: boolean;
|
||||
hiddenComponent: any;
|
||||
systemComponent: SystemComponentConfig;
|
||||
} = reactive({
|
||||
schemas: [],
|
||||
showResetButton: false,
|
||||
showSubmitButton: false,
|
||||
hiddenComponent: [],
|
||||
systemComponent: {
|
||||
functionalModule: '',
|
||||
functionName: '',
|
||||
functionFormName: '',
|
||||
},
|
||||
});
|
||||
const formData = reactive<Recordable>({});
|
||||
async function open() {
|
||||
if (props.item.formType == FormType.CUSTOM) {
|
||||
getFormProps.schemas = await getSchemasList(props.item.formId, props.item.formType);
|
||||
} else {
|
||||
let systemJson = await getSchemasList(props.item.formId, props.item.formType);
|
||||
getFormProps.systemComponent = systemJson.systemComponent;
|
||||
}
|
||||
visible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.form {
|
||||
min-height: 400px;
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="title">
|
||||
<NodeHead :node-name="t('表单列表')" />
|
||||
<a-button class="selected-btn" @click="open">{{ t('已选表单') }}</a-button>
|
||||
</div>
|
||||
<a-drawer
|
||||
v-if="visible"
|
||||
placement="right"
|
||||
:visible="true"
|
||||
:get-container="false"
|
||||
:closable="false"
|
||||
:mask="false"
|
||||
>
|
||||
<div class="selected-head title">
|
||||
<NodeHead :node-name="t('已选表单')" />
|
||||
<div class="close-icon" @click="close">+</div>
|
||||
</div>
|
||||
<div class="list-box" v-if="props.list.length > 0">
|
||||
<FormCard
|
||||
v-for="(item, index) in props.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
class="picked"
|
||||
@click="abolish(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="true" />
|
||||
</template>
|
||||
</FormCard>
|
||||
</div>
|
||||
<EmptyBox v-else />
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { NodeHead, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import { FormSettingItem } from '/@/model/workflow/formSetting';
|
||||
import FormCard from '/@bpmn/components/card/FormCard.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let emits = defineEmits(['abolish']);
|
||||
let props = withDefaults(defineProps<{ type: string; list: Array<FormSettingItem> }>(), {
|
||||
type: '',
|
||||
list: () => {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
|
||||
let visible = ref(false);
|
||||
|
||||
function open() {
|
||||
visible.value = true;
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
function abolish(item) {
|
||||
emits('abolish', item);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-content-wrapper) {
|
||||
width: 100% !important;
|
||||
box-shadow: 0 2px 2px 2px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-open) {
|
||||
position: absolute;
|
||||
width: calc(100% - 244px) !important;
|
||||
top: 50px;
|
||||
left: 240px;
|
||||
box-shadow: -5px 5px 4px 1px rgb(0 0 0 / 6%);
|
||||
height: calc(100% - 110px);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.selected-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.selected-btn {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
font-size: 24px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,500 @@
|
||||
<template>
|
||||
<div class="list-box">
|
||||
<div class="opr-box">
|
||||
<NodeHead :nodeName="t('表单列表')" />
|
||||
<div class="button-box">
|
||||
<SettingModal :list="[]" :isSingle="false" @submit="addItem">
|
||||
<a-button type="primary">{{ t('添加表单') }}</a-button>
|
||||
</SettingModal>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="row head">
|
||||
<span class="up-or-down"></span>
|
||||
<span class="form-name">{{ t('表单名称') }}</span>
|
||||
<span class="form-type">{{ t('表单类型') }}</span>
|
||||
<span class="opr">{{ t('操作') }}</span>
|
||||
</div>
|
||||
<div class="body" v-if="formSetting.list.length > 0">
|
||||
<div class="box" v-for="(item, index) in formSetting.list" :key="index">
|
||||
<div class="row item">
|
||||
<div class="up-or-down" @click="item.showChildren = !item.showChildren">
|
||||
<Icon
|
||||
class="icon"
|
||||
:icon="item.showChildren ? 'ant-design:up-outlined' : 'ant-design:down-outlined'"
|
||||
/>
|
||||
</div>
|
||||
<span class="form-name">{{ item.formName }}</span>
|
||||
<span class="form-type">{{ formTypeOptions[item.formType] }}</span>
|
||||
<span class="opr">
|
||||
<a-popconfirm @confirm="deleteItem(index)">
|
||||
<template #title>
|
||||
<div style="width: 300px">
|
||||
<p>{{ t('删除表单') }}</p>
|
||||
<p>{{ t('删除表单会清空已引用该表单数据的所有配置,请确认是否继续?') }}</p>
|
||||
<p class="pop-desc">{{
|
||||
t('如果引用该表单的配置较多,清空时间会相应变长,请耐心等待。')
|
||||
}}</p>
|
||||
</div>
|
||||
</template>
|
||||
<Icon icon="ant-design:delete-outlined" class="delete-icon" />
|
||||
</a-popconfirm>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="children list"
|
||||
v-if="item.showChildren && item.children && item.children.length > 0"
|
||||
>
|
||||
<div class="row head">
|
||||
<span class="flex-baisc-4"></span>
|
||||
<span>{{ t('字段名') }}</span>
|
||||
<span>{{ t('字段ID') }}</span>
|
||||
<span
|
||||
><a-checkbox
|
||||
v-model:checked="item.requiredAll"
|
||||
size="small"
|
||||
@change="requiredAll(item)"
|
||||
>{{ t('必填') }}</a-checkbox
|
||||
></span
|
||||
>
|
||||
<span
|
||||
><a-checkbox v-model:checked="item.viewAll" size="small" @change="viewAll(item)">{{
|
||||
t('查看')
|
||||
}}</a-checkbox></span
|
||||
>
|
||||
<span
|
||||
><a-checkbox v-model:checked="item.editAll" size="small" @change="editAll(item)">{{
|
||||
t('编辑')
|
||||
}}</a-checkbox></span
|
||||
>
|
||||
</div>
|
||||
<div class="body" v-if="item.children.length > 0">
|
||||
<div
|
||||
v-for="(child, childIndex) in item.children"
|
||||
:key="childIndex"
|
||||
class="padding-left"
|
||||
>
|
||||
<div class="row item" v-if="child.type != hiddenComponentType">
|
||||
<span
|
||||
><em
|
||||
class="flex-baisc-4"
|
||||
v-if="child.isSubTable"
|
||||
@click="child.showChildren = !child.showChildren"
|
||||
>
|
||||
<Icon
|
||||
class="icon"
|
||||
:icon="
|
||||
child.showChildren ? 'ant-design:up-outlined' : 'ant-design:down-outlined'
|
||||
"
|
||||
/> </em
|
||||
>{{ child.fieldName }}</span
|
||||
>
|
||||
<span>{{ child.fieldId }}</span>
|
||||
<span>
|
||||
<a-switch
|
||||
v-if="!child.isSubTable && !doNotShowControl.includes(child.type)"
|
||||
v-model:checked="child.required"
|
||||
:disabled="requiredDisabled.includes(child.type) || child.isSaveTable"
|
||||
@change="TableRequired(child.required, child, item)"
|
||||
size="small"
|
||||
/></span>
|
||||
<span>
|
||||
<a-switch
|
||||
v-model:checked="child.view"
|
||||
size="small"
|
||||
@change="TableView(child.view, child, item)"
|
||||
/></span>
|
||||
<span
|
||||
><a-switch
|
||||
v-if="!child.isSubTable && !doNotShowControl.includes(child.type)"
|
||||
:disabled="child.disabled"
|
||||
v-model:checked="child.edit"
|
||||
@change="TableEdit(child.edit, child, item)"
|
||||
size="small"
|
||||
/></span>
|
||||
</div>
|
||||
<template
|
||||
v-if="child.showChildren && child.isSubTable && child.children.length > 0"
|
||||
>
|
||||
<div
|
||||
class="row item padding-left"
|
||||
v-for="(child2, childIndex2) in child.children"
|
||||
:key="childIndex2"
|
||||
>
|
||||
<span>{{ child2.fieldName }}</span>
|
||||
<span>{{ child2.fieldId }}</span>
|
||||
<span
|
||||
><a-switch
|
||||
:disabled="child2.disabled || child2.isSaveTable"
|
||||
v-model:checked="child2.required"
|
||||
@change="TableRequired(child2.required, child2, item, child)"
|
||||
size="small"
|
||||
/></span>
|
||||
<span
|
||||
><a-switch
|
||||
:disabled="child2.disabled"
|
||||
v-model:checked="child2.view"
|
||||
@change="TableView(child2.view, child2, item, child)"
|
||||
size="small"
|
||||
/></span>
|
||||
<span
|
||||
><a-switch
|
||||
:disabled="child2.disabled || child2.isSaveTable"
|
||||
v-model:checked="child2.edit"
|
||||
@change="TableEdit(child2.edit, child2, item, child)"
|
||||
size="small"
|
||||
/></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<EmptyBox v-else :has-icon="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useStateFormInfo from '/@bpmn/hooks/useStateFormInfo';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import { NodeHead, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import SettingModal from '/@bpmn/components/formSettings/SettingModal.vue';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { FormConfigItem } from '/@/model/workflow/formSetting';
|
||||
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import {
|
||||
formPermissionList,
|
||||
hiddenComponentType,
|
||||
requiredDisabled,
|
||||
doNotShowControl,
|
||||
} from '/@bpmn/config/formPermission';
|
||||
import { updateFormDataInfo } from '/@bpmn/config/useUpdateAllFormInfo';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const { formInfo } = useStateFormInfo();
|
||||
const formTypeOptions = {};
|
||||
formTypeOptions[FormType.CUSTOM] = t('自定义表单');
|
||||
formTypeOptions[FormType.SYSTEM] = t('系统表单');
|
||||
let formSetting: { list: Array<FormConfigItem> } = reactive({ list: [] });
|
||||
|
||||
onMounted(() => {
|
||||
if (formInfo.value.formConfigs) formSetting.list = formInfo.value.formConfigs;
|
||||
formSetting.list.forEach((val) => {
|
||||
checkIsAll(val);
|
||||
});
|
||||
});
|
||||
function deleteItem(index: number) {
|
||||
updateFormDataInfo(formSetting.list[index]['key']);
|
||||
formSetting.list.splice(index, 1);
|
||||
}
|
||||
|
||||
async function addItem(list: Array<FormConfigItem>) {
|
||||
let returnArr = await formPermissionList(list);
|
||||
returnArr.forEach((val) => {
|
||||
checkIsAll(val);
|
||||
});
|
||||
formSetting.list.push(...returnArr);
|
||||
}
|
||||
|
||||
function requiredAll(item) {
|
||||
// 必填所有为true, 所有的必填,编辑,查看都为true(除了disabled)
|
||||
// 必填所有为false, 所有必填都取消(除了disabled)
|
||||
|
||||
let requiredAll = item.requiredAll;
|
||||
if (requiredAll) {
|
||||
item.viewAll = true;
|
||||
item.editAll = true;
|
||||
}
|
||||
item.children = item.children.map((ele) => {
|
||||
if (!ele.disabled) {
|
||||
if (requiredAll) {
|
||||
ele.required = true;
|
||||
ele.edit = true;
|
||||
} else {
|
||||
ele.required = false;
|
||||
}
|
||||
}
|
||||
if (requiredAll) ele.view = true;
|
||||
if (ele.children && ele.children.length > 0) {
|
||||
ele.children.map(
|
||||
(ele2: { disabled: any; required: boolean; view: boolean; edit: boolean }) => {
|
||||
if (!ele2.disabled) {
|
||||
if (requiredAll) {
|
||||
ele2.required = true;
|
||||
ele2.edit = true;
|
||||
} else {
|
||||
ele2.required = false;
|
||||
}
|
||||
}
|
||||
if (requiredAll) ele2.view = true;
|
||||
},
|
||||
);
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
|
||||
function editAll(item) {
|
||||
// 编辑所有为true, 所有的编辑,查看都为true(除了disabled)
|
||||
// 编辑所有为false, 所有编辑都为false(除了disabled)
|
||||
let editAll = item.editAll;
|
||||
if (editAll) {
|
||||
item.viewAll = true;
|
||||
} else {
|
||||
item.requiredAll = false;
|
||||
}
|
||||
item.children = item.children.map((ele) => {
|
||||
if (!ele.disabled) {
|
||||
if (editAll) {
|
||||
ele.edit = true;
|
||||
} else {
|
||||
ele.edit = false;
|
||||
ele.required = false;
|
||||
}
|
||||
}
|
||||
if (editAll) ele.view = true;
|
||||
if (ele.children && ele.children.length > 0) {
|
||||
ele.children.map(
|
||||
(ele2: { disabled: any; required: boolean; view: boolean; edit: boolean }) => {
|
||||
if (!ele2.disabled) {
|
||||
if (editAll) {
|
||||
ele2.edit = true;
|
||||
} else {
|
||||
ele2.edit = false;
|
||||
ele2.required = false;
|
||||
}
|
||||
}
|
||||
if (editAll) ele2.view = true;
|
||||
},
|
||||
);
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
function viewAll(item) {
|
||||
// 查看所有为true, 所有的查看都为true(除了disabled)
|
||||
// 查看所有为false, 所有查看都为false(除了disabled)
|
||||
let viewAll = item.viewAll;
|
||||
if (!viewAll) {
|
||||
item.requiredAll = false;
|
||||
item.editAll = false;
|
||||
}
|
||||
item.children = item.children.map((ele) => {
|
||||
if (viewAll) {
|
||||
ele.view = true;
|
||||
} else {
|
||||
ele.view = false;
|
||||
ele.edit = false;
|
||||
ele.required = false;
|
||||
}
|
||||
if (ele.children && ele.children.length > 0) {
|
||||
ele.children.map((ele2) => {
|
||||
if (viewAll) {
|
||||
ele2.view = true;
|
||||
} else {
|
||||
ele2.view = false;
|
||||
ele2.edit = false;
|
||||
ele2.required = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
return ele;
|
||||
});
|
||||
}
|
||||
function TableRequired(required, child, item, cur?) {
|
||||
if (required) {
|
||||
child.view = true;
|
||||
child.edit = true;
|
||||
}
|
||||
checkIsAll(item);
|
||||
if (cur) {
|
||||
checkCurTableIsEdit(cur);
|
||||
}
|
||||
}
|
||||
function TableView(view, child, item, cur?) {
|
||||
if (!view) {
|
||||
child.required = view;
|
||||
child.view = view;
|
||||
child.edit = view;
|
||||
}
|
||||
|
||||
if (child.children?.length > 0) {
|
||||
child.children.forEach((o) => {
|
||||
if (!view) {
|
||||
o.required = view;
|
||||
o.view = view;
|
||||
o.edit = view;
|
||||
} else {
|
||||
o.view = view;
|
||||
o.edit = view;
|
||||
}
|
||||
});
|
||||
}
|
||||
checkIsAll(item);
|
||||
if (cur) {
|
||||
checkCurTableIsEdit(cur);
|
||||
}
|
||||
}
|
||||
function TableEdit(edit, child, item, cur?) {
|
||||
if (edit) {
|
||||
child.view = true;
|
||||
} else {
|
||||
child.required = false;
|
||||
}
|
||||
checkIsAll(item);
|
||||
if (cur) {
|
||||
checkCurTableIsEdit(cur);
|
||||
}
|
||||
}
|
||||
function checkCurTableIsEdit(item) {
|
||||
let view = 0;
|
||||
let edit = 0;
|
||||
item.children.map((ele) => {
|
||||
if (ele.view) view += 1;
|
||||
if (ele.edit || ele.disabled) edit += 1;
|
||||
});
|
||||
|
||||
item.view = view > 0 ? true : false;
|
||||
item.edit = edit > 0 ? true : false;
|
||||
}
|
||||
function checkIsAll(item) {
|
||||
let all = 0;
|
||||
let required = 0;
|
||||
let view = 0;
|
||||
let edit = 0;
|
||||
item.children.map((ele) => {
|
||||
all += 1;
|
||||
if (ele.required || ele.disabled) required += 1;
|
||||
if (ele.view) view += 1;
|
||||
if (ele.edit || ele.disabled) edit += 1;
|
||||
|
||||
if (ele.children && ele.children.length > 0) {
|
||||
let childView = 0;
|
||||
ele.children.map((ele2) => {
|
||||
all += 1;
|
||||
if (ele2.required || ele2.disabled) required += 1;
|
||||
if (ele2.view) {
|
||||
view += 1;
|
||||
childView += 1;
|
||||
}
|
||||
if (ele2.edit || ele2.disabled) edit += 1;
|
||||
});
|
||||
ele.view = childView > 0 ? true : false;
|
||||
}
|
||||
});
|
||||
|
||||
item.requiredAll = required == all ? true : false;
|
||||
item.viewAll = view == all ? true : false;
|
||||
item.editAll = edit == all ? true : false;
|
||||
|
||||
// if (item.editAll) {
|
||||
// item.viewAll = true;
|
||||
// viewAll(item);
|
||||
// }
|
||||
//if (item.requiredAll) {
|
||||
// item.viewAll = true;
|
||||
// item.editAll = true;
|
||||
// viewAll(item);
|
||||
// editAll(item);
|
||||
//}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
.opr-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
.row {
|
||||
height: 40px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
flex-basis: 20%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.up-or-down {
|
||||
flex-basis: 30px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.form-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.form-type {
|
||||
flex-basis: 80px;
|
||||
}
|
||||
|
||||
.opr {
|
||||
flex-basis: 80px;
|
||||
}
|
||||
|
||||
.small {
|
||||
flex-basis: 60px;
|
||||
}
|
||||
|
||||
.flex-baisc-35 {
|
||||
flex-basis: 120px;
|
||||
}
|
||||
|
||||
.flex-baisc-4 {
|
||||
flex-basis: 40px;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.padding-left {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
.head {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: @clear-color;
|
||||
}
|
||||
|
||||
.empty-box {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid #f9f9f9;
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.slot-item) {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.pop-desc {
|
||||
font-size: 12px;
|
||||
color: rgb(0 0 0 / 40%);
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,424 @@
|
||||
<template>
|
||||
<div @click="show" class="slot-item">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<ModalPanel :visible="visible" :title="t('表单设置')" @submit="submit" @close="close">
|
||||
<template #left>
|
||||
<Category v-model="setting.checkFormType" @change="changeType" />
|
||||
</template>
|
||||
<SelectedList type="form" :list="setting.selectedList" @abolish="abolishChecked" />
|
||||
<SearchBox @search="searchList" />
|
||||
<div class="flow-used" v-if="setting.checkFormType == FormType.WORKFLOW">
|
||||
<EmptyBox v-if="showUsedEmpty" />
|
||||
<!-- 仅仅显示未重复表单 -->
|
||||
<div class="list-page-box" v-if="showRepeatedList">
|
||||
<FormCard
|
||||
v-for="(noUsedItem, index) in setting.usedNotRepeatedList"
|
||||
:key="index"
|
||||
:item="setting.formKeyList[noUsedItem]"
|
||||
@click="usedChecked(noUsedItem)"
|
||||
:class="selectKeys.includes(noUsedItem) ? 'picked' : 'notPicked'"
|
||||
class="notPicked"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="selectKeys.includes(noUsedItem)" />
|
||||
</template>
|
||||
</FormCard>
|
||||
</div>
|
||||
<!-- 既有重复表单又有未重复表单 显示面板折叠分类 -->
|
||||
<a-collapse v-else v-model:activeKey="activeKey" ghost expandIconPosition="right">
|
||||
<a-collapse-panel
|
||||
v-for="(formIds, formIndex) in setting.usedRepeatedList"
|
||||
:key="formIndex"
|
||||
:header="t('相同表单重复选用-') + setting.formKeyList[formIds[0]].name"
|
||||
>
|
||||
<div class="list-page-box" v-if="formIds.length > 0">
|
||||
<FormCard
|
||||
v-for="(key, index) in formIds"
|
||||
:key="index"
|
||||
:item="setting.formKeyList[key]"
|
||||
:class="selectKeys.includes(key) ? 'picked' : 'notPicked'"
|
||||
@click="usedChecked(key)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="selectKeys.includes(key)" />
|
||||
</template>
|
||||
</FormCard>
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel
|
||||
key="2000"
|
||||
:header="t('未重复选用的表单')"
|
||||
v-if="setting.usedNotRepeatedList.length > 0"
|
||||
>
|
||||
<div class="list-page-box">
|
||||
<FormCard
|
||||
v-for="(noUsedItem, index) in setting.usedNotRepeatedList"
|
||||
:key="index"
|
||||
:item="setting.formKeyList[noUsedItem]"
|
||||
@click="usedChecked(noUsedItem)"
|
||||
:class="selectKeys.includes(noUsedItem) ? 'picked' : 'notPicked'"
|
||||
class="notPicked"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="selectKeys.includes(noUsedItem)" />
|
||||
</template>
|
||||
</FormCard>
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="list-page-box" v-if="setting.list.length > 0">
|
||||
<FormCard
|
||||
v-for="(item, index) in setting.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:class="setting.checkedFormId[item.formId] ? 'picked' : 'notPicked'"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox
|
||||
size="small"
|
||||
:checked="setting.checkedFormId[item.formId] ? true : false"
|
||||
:disabled="item.enabledMark == 1 ? false : true"
|
||||
/>
|
||||
</template>
|
||||
</FormCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="setting.page.current"
|
||||
:pageSize="setting.page.pageSize"
|
||||
:total="setting.page.total"
|
||||
show-less-items
|
||||
@change="getList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<EmptyBox v-else />
|
||||
</template>
|
||||
</ModalPanel>
|
||||
<a-modal
|
||||
v-model:visible="setting.operationVisible"
|
||||
:width="400"
|
||||
:title="t('操作确认')"
|
||||
@ok="submitOperation"
|
||||
>
|
||||
<div class="opr-box">
|
||||
<div class="opr-icon">
|
||||
<Icon icon="ant-design:question-circle-outlined" :size="30" color="#ff9900" />
|
||||
</div>
|
||||
<div class="opr-content">
|
||||
<div class="title">{{ t('该表单已经在流程中引用过,是否重复引用?') }}</div>
|
||||
<div class="desc">{{ t('重复引用是在发起或审批中,会出现两张相同结构的表单。') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { ModalPanel, SearchBox, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import Category from '/@bpmn/components/formSettings/Category.vue';
|
||||
import SelectedList from '/@bpmn/components/formSettings/SelectedList.vue';
|
||||
import FormCard from '/@bpmn/components/card/FormCard.vue';
|
||||
import { FormType } from '/@/enums/workflowEnum';
|
||||
import { FormSettingItem } from '/@/model/workflow/formSetting';
|
||||
import { getFormTemplateEnabledPage } from '/@/api/form/design';
|
||||
import { randomFormNameStr, randomTime } from '/@bpmn/util/random';
|
||||
import { getUsedFormIds, getUsedFormList } from '/@bpmn/config/info';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let props = withDefaults(defineProps<{ isSingle: Boolean; list: Array<FormSettingItem> }>(), {
|
||||
isSingle: () => {
|
||||
return false;
|
||||
},
|
||||
list: () => {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
let emits = defineEmits(['submit']);
|
||||
const activeKey = ref(['2000']);
|
||||
|
||||
const visible = ref(false);
|
||||
let setting: {
|
||||
checkFormType: FormType;
|
||||
selectedList: Array<FormSettingItem>;
|
||||
list: Array<FormSettingItem>;
|
||||
searchKeyword: string;
|
||||
page: { current: number; total: number; pageSize: number };
|
||||
formKeyList: Object;
|
||||
checkedFormId: Object;
|
||||
usedRepeatedList: Object;
|
||||
usedNotRepeatedList: Array<string>;
|
||||
operationVisible: boolean;
|
||||
operationCloneItem: FormSettingItem;
|
||||
} = reactive({
|
||||
checkFormType: FormType.CUSTOM,
|
||||
selectedList: [],
|
||||
list: [],
|
||||
searchKeyword: '',
|
||||
page: { current: 1, total: 0, pageSize: 9 },
|
||||
formKeyList: {},
|
||||
checkedFormId: {},
|
||||
usedRepeatedList: {},
|
||||
usedNotRepeatedList: [],
|
||||
operationVisible: false,
|
||||
operationCloneItem: {
|
||||
key: '', //formId_key
|
||||
formType: FormType.CUSTOM, //表单类型
|
||||
formId: '', //表单ID 系统表单为文件夹名
|
||||
formName: '', //表单名称
|
||||
},
|
||||
});
|
||||
const showRepeatedList = computed(() => {
|
||||
return (
|
||||
setting.usedNotRepeatedList.length != 0 && JSON.stringify(setting.usedRepeatedList) == '{}'
|
||||
);
|
||||
});
|
||||
const showUsedEmpty = computed(() => {
|
||||
return (
|
||||
setting.usedNotRepeatedList.length == 0 && JSON.stringify(setting.usedRepeatedList) == '{}'
|
||||
);
|
||||
});
|
||||
let selectKeys = computed(() => {
|
||||
if (setting.selectedList && setting.selectedList.length > 0) {
|
||||
return setting.selectedList.map((ele: FormSettingItem) => {
|
||||
return ele.key;
|
||||
});
|
||||
}
|
||||
return [];
|
||||
});
|
||||
function show() {
|
||||
setting.checkFormType = FormType.CUSTOM;
|
||||
setting.list = [];
|
||||
setting.page.total = 0;
|
||||
setting.selectedList = [];
|
||||
getList();
|
||||
visible.value = true;
|
||||
}
|
||||
function changeType() {
|
||||
setting.list = [];
|
||||
setting.page.total = 0;
|
||||
getList();
|
||||
}
|
||||
async function getList() {
|
||||
if (setting.checkFormType == FormType.WORKFLOW) {
|
||||
getUsedList();
|
||||
} else {
|
||||
await getTemplateList();
|
||||
}
|
||||
}
|
||||
function getUsedList() {
|
||||
let { formKeyList, repeatedList, notRepeatedList } = getUsedFormList();
|
||||
setting.formKeyList = formKeyList;
|
||||
setting.usedNotRepeatedList = notRepeatedList;
|
||||
setting.usedRepeatedList = repeatedList;
|
||||
let arr: Array<string> = [];
|
||||
for (let k of Object.keys(setting.usedRepeatedList)) {
|
||||
arr.push(k);
|
||||
}
|
||||
arr.push('2000'); //未重复的表单的卡片key
|
||||
activeKey.value = arr;
|
||||
}
|
||||
async function getTemplateList() {
|
||||
let params = {
|
||||
limit: setting.page.current,
|
||||
size: setting.page.pageSize,
|
||||
type: setting.checkFormType,
|
||||
keyword: setting.searchKeyword,
|
||||
};
|
||||
let res = await getFormTemplateEnabledPage(params);
|
||||
if (res.total) {
|
||||
setting.page.total = res.total;
|
||||
}
|
||||
setting.list = [];
|
||||
if (res.list.length > 0) {
|
||||
res.list.forEach((ele) => {
|
||||
if (setting.checkFormType == FormType.CUSTOM) {
|
||||
setting.list.push({
|
||||
key: 'form_' + ele.id + '_' + randomTime(),
|
||||
formType: FormType.CUSTOM,
|
||||
formName: ele.name ? ele.name : ele.id,
|
||||
formId: ele.id,
|
||||
enabledMark: ele.enabledMark,
|
||||
});
|
||||
} else {
|
||||
setting.list.push({
|
||||
key: 'form_' + ele.id + '_' + randomTime(),
|
||||
formType: FormType.SYSTEM,
|
||||
formName: ele.name,
|
||||
formId: ele.id,
|
||||
enabledMark: ele.enabledMark,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
if (setting.selectedList.length === 0) {
|
||||
message.error(t('请至少选择一个表单'));
|
||||
return false;
|
||||
}
|
||||
emits('submit', setting.selectedList);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
function searchList(keyword: string) {
|
||||
setting.searchKeyword = keyword;
|
||||
setting.page.current = 1;
|
||||
getList();
|
||||
}
|
||||
function checked(item: FormSettingItem) {
|
||||
if (item.enabledMark != 1) return;
|
||||
let cloneItem: FormSettingItem = cloneDeep(item);
|
||||
cloneItem.formName = hasFormIdFromSelectedList(cloneItem.formId)
|
||||
? cloneItem.formName + '_' + randomFormNameStr()
|
||||
: cloneItem.formName;
|
||||
cloneItem.key = 'form_' + cloneItem.formId + '_' + randomTime();
|
||||
setting.operationCloneItem = cloneItem;
|
||||
if (props.isSingle) {
|
||||
// 单选
|
||||
setting.selectedList = [];
|
||||
setting.checkedFormId = {};
|
||||
if (!setting.checkedFormId[item.formId]) {
|
||||
if (hasFormIdFromSelectedList(cloneItem.formId)) {
|
||||
setting.operationVisible = true;
|
||||
} else {
|
||||
submitOperation();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 多选
|
||||
if (setting.checkedFormId[item.formId]) {
|
||||
// 取消选中
|
||||
let formKey = setting.checkedFormId[item.formId];
|
||||
if (selectKeys.value.includes(formKey)) {
|
||||
let index = setting.selectedList.findIndex((ele) => {
|
||||
return ele.key === formKey;
|
||||
});
|
||||
setting.selectedList.splice(index, 1);
|
||||
}
|
||||
setting.checkedFormId[item.formId] = '';
|
||||
} else {
|
||||
if (hasFormIdFromSelectedList(cloneItem.formId)) {
|
||||
setting.operationVisible = true;
|
||||
} else {
|
||||
submitOperation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function usedChecked(key: string) {
|
||||
if (props.isSingle) {
|
||||
let cloneItem: FormSettingItem = cloneDeep(setting.formKeyList[key]);
|
||||
setting.selectedList = [cloneItem];
|
||||
} else {
|
||||
if (selectKeys.value.includes(key)) {
|
||||
let index = setting.selectedList.findIndex((ele) => {
|
||||
return ele.key === key;
|
||||
});
|
||||
setting.selectedList.splice(index, 1);
|
||||
} else {
|
||||
let cloneItem: FormSettingItem = cloneDeep(setting.formKeyList[key]);
|
||||
setting.selectedList.push(cloneItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
function hasFormIdFromSelectedList(formId) {
|
||||
let usedIds = [
|
||||
...getUsedFormIds(),
|
||||
...setting.selectedList.map((ele) => {
|
||||
return ele.formId;
|
||||
}),
|
||||
];
|
||||
return usedIds.includes(formId);
|
||||
}
|
||||
function submitOperation() {
|
||||
let cloneItem: FormSettingItem = setting.operationCloneItem;
|
||||
setting.checkedFormId[cloneItem.formId] = cloneItem.key;
|
||||
setting.selectedList.push(cloneItem);
|
||||
setting.operationVisible = false;
|
||||
}
|
||||
|
||||
function abolishChecked(item: FormSettingItem) {
|
||||
setting.selectedList = setting.selectedList.filter((ele) => {
|
||||
return ele.key != item.key;
|
||||
});
|
||||
setting.checkedFormId[item.formId] = '';
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.page-box {
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
|
||||
.notPicked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.flow-used {
|
||||
height: calc(100% - 100px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-ghost > .ant-collapse-item) {
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-icon-position-right > .ant-collapse-item > .ant-collapse-header) {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-content-box .ant-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
:deep(.ant-collapse-content-box) {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
:deep(.ant-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.opr-box {
|
||||
display: flex;
|
||||
padding: 30px 20px;
|
||||
|
||||
.opr-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.opr-content {
|
||||
.title {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.desc {
|
||||
color: #a0a0a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<SelectApiConfig
|
||||
v-model="config"
|
||||
:paramTree="ProcessDataVariable"
|
||||
:exampleStr="exampleStr"
|
||||
@update:modelValue="submit"
|
||||
>
|
||||
<slot></slot>
|
||||
</SelectApiConfig>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { SelectApiConfig } from '/@/components/ApiConfig';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { ApiConfig } from '/@/components/ApiConfig/src/interface';
|
||||
import { ProcessDataVariable } from '/@bpmn/config/rules';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emit = defineEmits(['change']);
|
||||
let props = withDefaults(defineProps<{ memberList: Array<MemberConfig> }>(), {
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
});
|
||||
let config = ref<ApiConfig>();
|
||||
const exampleStr = `{
|
||||
code: 0,
|
||||
msg: 'success',
|
||||
data: '1830999999983899309,2835993035383895389', //用户ID,可以多个,用逗号分隔
|
||||
}`;
|
||||
const memberType = MemberType.API;
|
||||
const { notification } = useMessage();
|
||||
function submit() {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList.length > 0) {
|
||||
list = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType != memberType) return ele;
|
||||
});
|
||||
}
|
||||
if (config.value?.path) {
|
||||
list.push({
|
||||
memberType: memberType,
|
||||
id: config.value.id,
|
||||
name: config.value.path,
|
||||
apiConfig: config.value,
|
||||
});
|
||||
emit('change', [...list]);
|
||||
} else {
|
||||
notification.warning({
|
||||
message: t('提示'),
|
||||
description: t('您没有选择API'),
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
185
src/views/workflow/design/bpmn/components/member/FormFields.vue
Normal file
185
src/views/workflow/design/bpmn/components/member/FormFields.vue
Normal file
@ -0,0 +1,185 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click="show"><slot></slot></div>
|
||||
<ModalPanel
|
||||
:visible="visible"
|
||||
:width="400"
|
||||
:title="t('表单字段配置人员')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<div class="title">
|
||||
<NodeHead :node-name="t('表单字段列表')" />
|
||||
</div>
|
||||
<div class="list-box" v-if="Object.keys(expandedNames).length > 0">
|
||||
<a-tree
|
||||
checkable
|
||||
:tree-data="nodes.treeData"
|
||||
autoExpandParent
|
||||
defaultExpandAll
|
||||
v-model:checkedKeys="checkedKeys"
|
||||
/>
|
||||
</div>
|
||||
<EmptyBox v-else />
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { BpmnNodeKey, MemberType } from '/@/enums/workflowEnum';
|
||||
import { FormFiledConfig, MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { ModalPanel, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { TreeProps } from 'ant-design-vue';
|
||||
import { separator } from '../../config/info';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const visible = ref(false);
|
||||
const memberType = MemberType.FORM_FIELD;
|
||||
let nodes: { treeData: TreeProps['treeData'] } = reactive({
|
||||
treeData: [],
|
||||
});
|
||||
const checkedKeys = ref<string[]>([]);
|
||||
const expandedNames = ref<{}>({});
|
||||
let selectedList: { list: Array<MemberConfig> } = reactive({ list: [] });
|
||||
function show() {
|
||||
checkedKeys.value = [];
|
||||
nodes.treeData = [];
|
||||
if (props.memberList.length > 0) {
|
||||
selectedList.list = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType === memberType) return ele;
|
||||
});
|
||||
|
||||
if (selectedList.list.length > 0) {
|
||||
selectedList.list.forEach((ele) => {
|
||||
if (ele.formFieldConfig) {
|
||||
checkedKeys.value.push(
|
||||
ele.formFieldConfig.nodeId +
|
||||
separator +
|
||||
ele.formFieldConfig.formId +
|
||||
separator +
|
||||
ele.formFieldConfig.formField +
|
||||
separator +
|
||||
ele.formFieldConfig.formKey,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const store = useBpmnStore();
|
||||
const { info } = store;
|
||||
for (let item of info.values()) {
|
||||
let showNodeTypes = [BpmnNodeKey.START, BpmnNodeKey.USER];
|
||||
if (showNodeTypes.includes(item.type)) {
|
||||
let name = item.name ? item.name : item.type;
|
||||
let id = item.id;
|
||||
let info: FormFiledConfig = {
|
||||
title: name,
|
||||
key: id,
|
||||
disabled: true,
|
||||
children: [],
|
||||
};
|
||||
if (item.formConfigs && item.formConfigs.length > 0) {
|
||||
item.formConfigs.forEach((ele1) => {
|
||||
let formChildren: Array<FormFiledConfig> = [];
|
||||
if (ele1.children && ele1.children.length > 0) {
|
||||
ele1.children.forEach((ele2) => {
|
||||
let key =
|
||||
id + separator + ele1.formId + separator + ele2.fieldId + separator + ele1.key;
|
||||
formChildren.push({
|
||||
title: ele2.fieldName,
|
||||
key,
|
||||
disabled: false,
|
||||
children: [],
|
||||
});
|
||||
expandedNames.value[key] = name + '-' + ele1.formName + '-' + ele2.fieldName;
|
||||
});
|
||||
}
|
||||
info.children.push({
|
||||
title: ele1.formName,
|
||||
key: id + separator + ele1.formId,
|
||||
disabled: true,
|
||||
children: formChildren,
|
||||
});
|
||||
});
|
||||
}
|
||||
nodes.treeData.push(info);
|
||||
}
|
||||
}
|
||||
|
||||
visible.value = true;
|
||||
}
|
||||
function submit() {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList.length > 0) {
|
||||
list = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType != memberType) return ele;
|
||||
});
|
||||
}
|
||||
console.log(checkedKeys.value, 'sss');
|
||||
for (const item of checkedKeys.value) {
|
||||
let arr = item.split(separator);
|
||||
if (arr.length === 4) {
|
||||
let nodeId = arr[0];
|
||||
let formId = arr[1];
|
||||
let formField = arr[2];
|
||||
let formKey = arr[3];
|
||||
list.push({
|
||||
memberType: memberType,
|
||||
id: nodeId,
|
||||
name: expandedNames.value[item],
|
||||
formFieldConfig: { nodeId, formId, formField, formKey },
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log(list, 'list');
|
||||
emits('change', [...list]);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-treenode) {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-list) {
|
||||
height: 460px;
|
||||
}
|
||||
</style>
|
||||
153
src/views/workflow/design/bpmn/components/member/MemberTable.vue
Normal file
153
src/views/workflow/design/bpmn/components/member/MemberTable.vue
Normal file
@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<div class="list-box">
|
||||
<div class="opr-box">
|
||||
<div class="header-box"><NodeHead :nodeName="t('人员列表')" /></div>
|
||||
<div class="button-box">
|
||||
<div v-for="(item, index) in allComponentList" :key="index">
|
||||
<component
|
||||
v-if="item.show"
|
||||
:is="item.component"
|
||||
:memberList="props.memberList"
|
||||
@change="changeList"
|
||||
>
|
||||
<a-button type="primary">{{ item.name }}</a-button>
|
||||
</component>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-table :dataSource="props.memberList" :columns="configColumns" :pagination="false">
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'memberType'">
|
||||
{{ getMemberType(record.memberType) }}
|
||||
</template>
|
||||
<template v-if="column.key === 'operation'">
|
||||
<Icon icon="ant-design:delete-outlined" class="delete-icon" @click="deleteItem(index)" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import Posts from '/@bpmn/components/member/Posts.vue';
|
||||
import Roles from '/@bpmn/components/member/Roles.vue';
|
||||
import Users from '/@bpmn/components/member/Users.vue';
|
||||
import NodeApprover from '/@bpmn/components/member/NodeApprover.vue';
|
||||
import UpperManagement from '/@bpmn/components/member/UpperManagement.vue';
|
||||
import FormFields from '/@bpmn/components/member/FormFields.vue';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import ApiSelect from '/@bpmn/components/member/ApiSelect.vue';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['update:memberList']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
isCommonType: Boolean;
|
||||
isUpper?: Boolean;
|
||||
isApiApprover?: Boolean;
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
isCommonType: () => false,
|
||||
isUpper: () => false,
|
||||
isApiApprover: () => false,
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
const configColumns = [
|
||||
{
|
||||
title: t('类型'),
|
||||
dataIndex: 'memberType',
|
||||
key: 'memberType',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('名称'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
align: 'center',
|
||||
},
|
||||
];
|
||||
|
||||
const allComponentList = computed(() => {
|
||||
return [
|
||||
{ name: t('添加岗位'), component: Posts, show: true },
|
||||
{ name: t('添加角色'), component: Roles, show: true },
|
||||
{ name: t('添加人员'), component: Users, show: true },
|
||||
{ name: t('节点审批人'), component: NodeApprover, show: props.isCommonType ? false : true },
|
||||
{
|
||||
name: t('上级领导'),
|
||||
component: UpperManagement,
|
||||
show: props.isCommonType ? false : props.isUpper ? false : true,
|
||||
},
|
||||
{ name: t('表单字段'), component: FormFields, show: props.isCommonType ? false : true },
|
||||
{ name: t('API审批人'), component: ApiSelect, show: props.isApiApprover ? true : false },
|
||||
];
|
||||
});
|
||||
// 类型
|
||||
function getMemberType(val: MemberType) {
|
||||
if (val === MemberType.POST) return t('岗位');
|
||||
if (val === MemberType.ROLE) return t('角色');
|
||||
if (val === MemberType.USER) return t('人员');
|
||||
if (val === MemberType.SPECIFY_NODE_APPROVER) return t('指定审批人');
|
||||
if (val === MemberType.SUPERIOR_LEADERS) return t('上级领导');
|
||||
if (val === MemberType.FORM_FIELD) return t('表单字段');
|
||||
if (val === MemberType.API) return t('API审批人');
|
||||
return val;
|
||||
}
|
||||
function changeList(list: Array<MemberConfig>) {
|
||||
emits('update:memberList', list);
|
||||
}
|
||||
function deleteItem(index: number) {
|
||||
let list = props.memberList;
|
||||
list.splice(index, 1);
|
||||
emits('update:memberList', list);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
.opr-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.header-box {
|
||||
flex-basis: 220px;
|
||||
align-items: flex-start;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.button-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
:deep(button) {
|
||||
margin: 4px;
|
||||
width: 88px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: @clear-color;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click="show"><slot></slot></div>
|
||||
<ModalPanel
|
||||
:visible="visible"
|
||||
:width="400"
|
||||
:title="t('指定节点审批人')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<div class="title">
|
||||
<NodeHead :node-name="t('节点列表')" />
|
||||
</div>
|
||||
<div class="list-box">
|
||||
<a-checkbox
|
||||
v-for="(item, index) in nodes.list"
|
||||
:key="index"
|
||||
:value="item.id"
|
||||
size="small"
|
||||
v-model:checked="item.checked"
|
||||
>{{ item.name }}</a-checkbox
|
||||
>
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig, NodesConfig } from '/@/model/workflow/memberSetting';
|
||||
import { NodeHead, ModalPanel } from '/@/components/ModalPanel/index';
|
||||
import { InfoId } from '/@/model/workflow/bpmnConfig';
|
||||
import { getNodeList } from '/@bpmn/config/info';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
const visible = ref(false);
|
||||
const memberType = MemberType.SPECIFY_NODE_APPROVER;
|
||||
let nodes: { list: Array<NodesConfig> } = reactive({
|
||||
list: [],
|
||||
});
|
||||
function show() {
|
||||
nodes.list = [];
|
||||
let selectIds: Array<InfoId> = [];
|
||||
if (props.memberList.length > 0) {
|
||||
selectIds = props.memberList
|
||||
.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType && ele.memberType === memberType) return ele;
|
||||
})
|
||||
.map((ele: MemberConfig) => {
|
||||
return ele.id;
|
||||
});
|
||||
}
|
||||
getNodeList().map((item) => {
|
||||
nodes.list.push({ id: item.id, name: item.name, checked: selectIds.includes(item.id) });
|
||||
});
|
||||
visible.value = true;
|
||||
}
|
||||
function submit() {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList.length > 0) {
|
||||
list = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType != memberType) return ele;
|
||||
});
|
||||
}
|
||||
nodes.list.forEach((item: NodesConfig) => {
|
||||
if (item.checked) {
|
||||
list.push({
|
||||
memberType: memberType,
|
||||
name: item.name,
|
||||
id: item.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
emits('change', [...list]);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-wrapper) {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-radius: unset;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
border-radius: unset;
|
||||
}
|
||||
</style>
|
||||
77
src/views/workflow/design/bpmn/components/member/Posts.vue
Normal file
77
src/views/workflow/design/bpmn/components/member/Posts.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div>
|
||||
<SelectPost :selectedIds="selectedIds" :multiple="true" @change="submit">
|
||||
<a-button type="primary">{{ t('添加岗位') }}</a-button>
|
||||
</SelectPost>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getPostMulti } from '/@/api/system/post';
|
||||
import { SelectPost } from '/@/components/SelectOrganizational/index';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let selectedIds = computed(() => {
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
return props.memberList
|
||||
.filter((ele: MemberConfig) => {
|
||||
return ele.memberType === MemberType.POST;
|
||||
})
|
||||
.map((ele: MemberConfig) => {
|
||||
return ele.id;
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
async function submit(ids: Array<string>) {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
props.memberList.forEach((ele) => {
|
||||
if (ele.memberType != MemberType.POST)
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: ele.memberType,
|
||||
});
|
||||
});
|
||||
}
|
||||
try {
|
||||
let postRes = await getPostMulti(ids.join(','));
|
||||
if (postRes.length > 0) {
|
||||
postRes.forEach((ele) => {
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: MemberType.POST,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ids.forEach((id) => {
|
||||
if (id)
|
||||
list.push({
|
||||
name: id,
|
||||
id: id,
|
||||
memberType: MemberType.POST,
|
||||
});
|
||||
});
|
||||
}
|
||||
emits('change', list);
|
||||
}
|
||||
</script>
|
||||
78
src/views/workflow/design/bpmn/components/member/Roles.vue
Normal file
78
src/views/workflow/design/bpmn/components/member/Roles.vue
Normal file
@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div>
|
||||
<SelectRole :selectedIds="selectedIds" :multiple="true" @change="submit">
|
||||
<a-button type="primary">{{ t('添加角色') }}</a-button>
|
||||
</SelectRole>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getRoleMulti } from '/@/api/system/role';
|
||||
import { SelectRole } from '/@/components/SelectOrganizational/index';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let selectedIds = computed(() => {
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
return props.memberList
|
||||
.filter((ele: MemberConfig) => {
|
||||
return ele.memberType === MemberType.ROLE;
|
||||
})
|
||||
.map((ele: MemberConfig) => {
|
||||
return ele.id;
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
async function submit(ids: Array<string>) {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
props.memberList.forEach((ele) => {
|
||||
if (ele.memberType != MemberType.ROLE)
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: ele.memberType,
|
||||
});
|
||||
});
|
||||
}
|
||||
try {
|
||||
let roleRes = await getRoleMulti(ids.join(','));
|
||||
if (roleRes.length > 0) {
|
||||
roleRes.forEach((ele) => {
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: MemberType.ROLE,
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ids.forEach((id) => {
|
||||
if (id)
|
||||
list.push({
|
||||
name: id,
|
||||
id: id,
|
||||
memberType: MemberType.ROLE,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
emits('change', list);
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,190 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click="show"><slot></slot></div>
|
||||
<ModalPanel
|
||||
:visible="visible"
|
||||
:width="400"
|
||||
:title="t('上级领导配置')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<div class="title">
|
||||
<NodeHead :node-name="t('上级领导列表')" />
|
||||
</div>
|
||||
<div class="list-box">
|
||||
<a-tree
|
||||
checkable
|
||||
:tree-data="data.treeData"
|
||||
autoExpandParent
|
||||
defaultExpandAll
|
||||
v-model:checkedKeys="data.checkedKeys"
|
||||
>
|
||||
<template #title="{ title, key }">
|
||||
<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
|
||||
<template v-else>{{ title }}</template>
|
||||
</template>
|
||||
</a-tree>
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { BpmnNodeKey, LevelEnum, MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { ModalPanel } from '/@/components/ModalPanel/index';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { TreeProps } from 'ant-design-vue';
|
||||
import { separator } from '../../config/info';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
const visible = ref(false);
|
||||
const memberType = MemberType.SUPERIOR_LEADERS;
|
||||
let data: {
|
||||
treeData: TreeProps['treeData'];
|
||||
checkedKeys: string[];
|
||||
expandedNames: Object;
|
||||
selected: Array<MemberConfig>;
|
||||
} = reactive({ treeData: [], checkedKeys: [], expandedNames: {}, selected: [] });
|
||||
|
||||
function show() {
|
||||
data.checkedKeys = [];
|
||||
data.treeData = [];
|
||||
if (props.memberList.length > 0) {
|
||||
data.selected = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType === memberType) return ele;
|
||||
});
|
||||
|
||||
if (data.selected.length > 0) {
|
||||
data.selected.forEach((ele) => {
|
||||
if (ele.leaderConfig?.level)
|
||||
data.checkedKeys.push(ele.leaderConfig.nodeId + separator + ele.leaderConfig.level);
|
||||
});
|
||||
}
|
||||
}
|
||||
const store = useBpmnStore();
|
||||
const { info } = store;
|
||||
for (let item of info.values()) {
|
||||
let showNodeTypes = [BpmnNodeKey.START, BpmnNodeKey.USER];
|
||||
if (showNodeTypes.includes(item.type)) {
|
||||
let name = item.name ? item.name : item.type;
|
||||
data.treeData.push({
|
||||
title: name,
|
||||
key: item.id,
|
||||
children: getLeaveChildren(item.id),
|
||||
});
|
||||
data.expandedNames[item.id] = name;
|
||||
}
|
||||
}
|
||||
|
||||
visible.value = true;
|
||||
}
|
||||
function submit() {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList.length > 0) {
|
||||
list = props.memberList.filter((ele: MemberConfig) => {
|
||||
if (ele.memberType != memberType) return ele;
|
||||
});
|
||||
}
|
||||
for (const leader of data.checkedKeys) {
|
||||
let arr = leader.split(separator);
|
||||
if (arr.length == 2) {
|
||||
let nodeId = arr[0];
|
||||
let level = Number(arr[1]);
|
||||
list.push({
|
||||
memberType: memberType,
|
||||
id: nodeId,
|
||||
name: data.expandedNames[nodeId] + '-' + getLeaveName(level),
|
||||
leaderConfig: { nodeId, level },
|
||||
});
|
||||
}
|
||||
}
|
||||
emits('change', [...list]);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
function getLeaveName(level: LevelEnum) {
|
||||
switch (level) {
|
||||
case LevelEnum.ONE:
|
||||
return '上一级领导';
|
||||
case LevelEnum.SECOND:
|
||||
return '上二级领导';
|
||||
case LevelEnum.THREE:
|
||||
return '上三级领导';
|
||||
case LevelEnum.FOUR:
|
||||
return '上四级领导';
|
||||
case LevelEnum.FIVE:
|
||||
return '上五级领导';
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
function getLeaveChildren(id: string) {
|
||||
return [
|
||||
{
|
||||
title: getLeaveName(LevelEnum.ONE),
|
||||
key: id + separator + LevelEnum.ONE,
|
||||
},
|
||||
{
|
||||
title: getLeaveName(LevelEnum.SECOND),
|
||||
key: id + separator + LevelEnum.SECOND,
|
||||
},
|
||||
{
|
||||
title: getLeaveName(LevelEnum.THREE),
|
||||
key: id + separator + LevelEnum.THREE,
|
||||
},
|
||||
{
|
||||
title: getLeaveName(LevelEnum.FOUR),
|
||||
key: id + separator + LevelEnum.FOUR,
|
||||
},
|
||||
{
|
||||
title: getLeaveName(LevelEnum.FIVE),
|
||||
key: id + separator + LevelEnum.FIVE,
|
||||
},
|
||||
];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-treenode) {
|
||||
margin-left: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-list) {
|
||||
height: 460px;
|
||||
}
|
||||
</style>
|
||||
66
src/views/workflow/design/bpmn/components/member/Users.vue
Normal file
66
src/views/workflow/design/bpmn/components/member/Users.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div>
|
||||
<SelectUser :selectedIds="selectedIds" :multiple="true" @change="submit">
|
||||
<a-button type="primary">{{ t('添加人员') }}</a-button>
|
||||
</SelectUser>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getUserMulti } from '/@/api/system/user';
|
||||
import { SelectUser } from '/@/components/SelectOrganizational/index';
|
||||
import { MemberType } from '/@/enums/workflowEnum';
|
||||
import { MemberConfig } from '/@/model/workflow/memberSetting';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
memberList: Array<MemberConfig>;
|
||||
}>(),
|
||||
{
|
||||
memberList: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let selectedIds = computed(() => {
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
return props.memberList
|
||||
.filter((ele: MemberConfig) => {
|
||||
return ele.memberType === MemberType.USER;
|
||||
})
|
||||
.map((ele: MemberConfig) => {
|
||||
return ele.id;
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
async function submit(ids: Array<string>) {
|
||||
let list: Array<MemberConfig> = [];
|
||||
if (props.memberList && props.memberList.length > 0) {
|
||||
props.memberList.forEach((ele) => {
|
||||
if (ele.memberType != MemberType.USER)
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: ele.memberType,
|
||||
});
|
||||
});
|
||||
}
|
||||
let users = await getUserMulti(ids.join(','));
|
||||
if (users.length > 0) {
|
||||
users.forEach((ele) => {
|
||||
list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
memberType: MemberType.USER,
|
||||
});
|
||||
});
|
||||
}
|
||||
emits('change', list);
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="list-box">
|
||||
<div class="opr-box">
|
||||
<NodeHead :nodeName="t('表单操作列表')" />
|
||||
<div class="button-box">
|
||||
<a-button type="primary" @click="addItem">{{ t('添加') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="row head">
|
||||
<span class="common">{{ t('赋值来源') }}</span>
|
||||
<span class="common">{{ t('目标表单字段') }}</span>
|
||||
<span class="small">{{ t('操作') }}</span>
|
||||
</div>
|
||||
<div class="body" v-if="formInfo.assignmentConfig.formAssignmentConfigs.length > 0">
|
||||
<div
|
||||
class="row item"
|
||||
v-for="(item, index) in formInfo.assignmentConfig.formAssignmentConfigs"
|
||||
:key="index"
|
||||
>
|
||||
<span class="common">
|
||||
<a-tree-select
|
||||
v-model:value="item.source"
|
||||
autoExpandParent
|
||||
treeDefaultExpandAll
|
||||
:tree-data="props.processParameter"
|
||||
style="width: 100%"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}"
|
||||
/>
|
||||
</span>
|
||||
<span class="common">
|
||||
<FormTargetItem v-model:target="item.target" :formSettingTree="props.formSettingTree" />
|
||||
</span>
|
||||
<span @click="deleteItem(index)" class="small">
|
||||
<Icon icon="ant-design:delete-outlined" class="delete-icon" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<EmptyBox v-else :has-icon="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NodeHead, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
import FormTargetItem from './FormTargetItem.vue';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
formSettingTree: Array,
|
||||
processParameter: Array,
|
||||
});
|
||||
|
||||
const store = useBpmnStore();
|
||||
const { infoId } = store;
|
||||
const { info } = storeToRefs(store);
|
||||
const formInfo = ref();
|
||||
|
||||
formInfo.value = info.value.get(infoId);
|
||||
|
||||
function addItem() {
|
||||
formInfo.value.assignmentConfig.formAssignmentConfigs.push({
|
||||
source: '',
|
||||
target: { key: '', formId: '', formField: '' },
|
||||
});
|
||||
}
|
||||
function deleteItem(index: number) {
|
||||
formInfo.value.assignmentConfig.formAssignmentConfigs.splice(index, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
.opr-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
.row {
|
||||
height: 40px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.common {
|
||||
flex-basis: 40%;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.small {
|
||||
flex-basis: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
.head {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: @clear-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<a-tree-select
|
||||
:value="props.target.key"
|
||||
autoExpandParent
|
||||
treeDefaultExpandAll
|
||||
:tree-data="props.formSettingTree"
|
||||
style="width: 100%"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
}"
|
||||
@select="changeTarget"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const emit = defineEmits(['update:target']);
|
||||
const props = defineProps({
|
||||
target: {
|
||||
type: Object, //类型
|
||||
default: null, //默认值
|
||||
},
|
||||
formSettingTree: {
|
||||
type: Array, //类型
|
||||
default: null, //默认值
|
||||
},
|
||||
});
|
||||
|
||||
function changeTarget(value, node) {
|
||||
let target = props.target;
|
||||
target.formId = node.formId;
|
||||
target.key = value;
|
||||
target.formField = node.formField;
|
||||
emit('update:target', target);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="list-box">
|
||||
<div class="opr-box">
|
||||
<NodeHead :nodeName="t('参数赋值列表')" />
|
||||
<div class="button-box">
|
||||
<a-button type="primary" @click="add">{{ t('添加') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div class="row head">
|
||||
<span class="source">{{ t('赋值来源') }}</span>
|
||||
<span class="target">{{ t('赋值配置') }}</span>
|
||||
<span class="config">{{ t('目标参数') }}</span>
|
||||
<span class="small">{{ t('操作') }}</span>
|
||||
</div>
|
||||
<div class="body" v-if="formInfo.assignmentConfig.paramAssignmentConfigs.length > 0">
|
||||
<div
|
||||
class="row item"
|
||||
v-for="(item, index) in formInfo.assignmentConfig.paramAssignmentConfigs"
|
||||
:key="index"
|
||||
>
|
||||
<span class="source"
|
||||
><a-select v-model:value="item.type" :placeholder="t('请选择')" style="width: 100%">
|
||||
<a-select-option :value="ParamType.VALUE"> {{ t('值') }} </a-select-option>
|
||||
<a-select-option :value="ParamType.VARIABLE"> {{ t('变量') }} </a-select-option>
|
||||
<a-select-option v-if="props.needFromData" :value="ParamType.FORM_DATA">
|
||||
{{ t('表单数据') }}
|
||||
</a-select-option>
|
||||
<a-select-option :value="ParamType.API"> API </a-select-option>
|
||||
</a-select></span
|
||||
>
|
||||
<span class="target">
|
||||
<a-input
|
||||
v-if="item.type == ParamType.VALUE"
|
||||
v-model:value="item.value"
|
||||
:placeholder="t('请填写参数值')"
|
||||
/>
|
||||
<a-tree-select
|
||||
v-if="item.type == ParamType.VARIABLE"
|
||||
v-model:value="item.varValue"
|
||||
autoExpandParent
|
||||
treeDefaultExpandAll
|
||||
:tree-data="variableTree"
|
||||
style="width: 100%"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
}"
|
||||
/>
|
||||
|
||||
<ScriptApiSelect
|
||||
style="width: 100%"
|
||||
v-if="item.type == ParamType.API"
|
||||
v-model="item.apiConfig"
|
||||
:need-hide-components="true"
|
||||
/>
|
||||
<FormTargetItem
|
||||
v-if="item.type == ParamType.FORM_DATA"
|
||||
v-model:target="item.formConfig"
|
||||
:formSettingTree="props.formSettingTree"
|
||||
/>
|
||||
</span>
|
||||
<span class="config">
|
||||
<a-tree-select
|
||||
v-model:value="item.target"
|
||||
autoExpandParent
|
||||
treeDefaultExpandAll
|
||||
:tree-data="props.processParameter"
|
||||
style="width: 100%"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}"
|
||||
/></span>
|
||||
<span @click="deleteItem(index)" class="small">
|
||||
<Icon icon="ant-design:delete-outlined" class="delete-icon" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<EmptyBox v-else :has-icon="false" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NodeHead, EmptyBox } from '/@/components/ModalPanel/index';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
//import ScriptApiSelect from '/@bpmn/components/arguments/ApiSelect.vue'; //之前是调的这个,现在换成ScriptApiSelect.vue
|
||||
import ScriptApiSelect from '/@bpmn/components/arguments/ScriptApiSelect.vue';
|
||||
import FormTargetItem from './FormTargetItem.vue';
|
||||
import { ProcessArgumentTreeData } from '/@bpmn/config/rules';
|
||||
import { ParamType } from '/@/enums/workflowEnum';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
formSettingTree: Array,
|
||||
processParameter: Array,
|
||||
needFromData: Boolean,
|
||||
});
|
||||
|
||||
const store = useBpmnStore();
|
||||
const { infoId } = store;
|
||||
const { info } = storeToRefs(store);
|
||||
const formInfo = ref();
|
||||
|
||||
formInfo.value = info.value.get(infoId);
|
||||
|
||||
const variableTree = ProcessArgumentTreeData;
|
||||
// 新增
|
||||
function add() {
|
||||
formInfo.value.assignmentConfig.paramAssignmentConfigs.push({
|
||||
type: ParamType.VARIABLE,
|
||||
value: '',
|
||||
varValue: '',
|
||||
apiConfig: {
|
||||
id: '',
|
||||
name: '',
|
||||
method: '',
|
||||
requestParamsConfigs: [], //Query Params
|
||||
requestHeaderConfigs: [], //Header
|
||||
requestBodyConfigs: [], //Body
|
||||
},
|
||||
formConfig: { key: '', formId: '', formField: '' },
|
||||
target: '',
|
||||
});
|
||||
}
|
||||
//删除
|
||||
function deleteItem(index: number) {
|
||||
formInfo.value.assignmentConfig.paramAssignmentConfigs.splice(index, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
.opr-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.list {
|
||||
.row {
|
||||
height: 40px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.source {
|
||||
flex-basis: 120px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.config {
|
||||
min-width: 120px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.target {
|
||||
flex: 1;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.small {
|
||||
flex-basis: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
.head {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.item {
|
||||
border-bottom: 1px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: @clear-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<a-tabs>
|
||||
<a-tab-pane key="1" :tab="t('表单赋值')">
|
||||
<FormAssignmentConfig :formSettingTree="targetTree" :processParameter="processParameter" />
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="t('参数赋值')">
|
||||
<ParamAssignmentConfig
|
||||
:needFromData="true"
|
||||
:processParameter="processParameter"
|
||||
:formSettingTree="sourceTree"
|
||||
/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { getFormSettingTree, getProcessParamConfigs } from '/@bpmn/config/info';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import FormAssignmentConfig from './FormAssignmentConfig.vue';
|
||||
import ParamAssignmentConfig from './ParamAssignmentConfig.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const bpmnStore = useBpmnStore();
|
||||
const targetTree = computed(() => {
|
||||
const { info, infoId } = bpmnStore;
|
||||
if (infoId && info.has(infoId)) {
|
||||
let value = info.get(infoId);
|
||||
if (value?.formConfigs) {
|
||||
return getFormSettingTree(value.formConfigs);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const sourceTree = computed(() => {
|
||||
const { info, infoId } = bpmnStore;
|
||||
if (infoId && info.has(infoId)) {
|
||||
let value = info.get(infoId);
|
||||
if (value?.formConfigs) {
|
||||
return getFormSettingTree(value.formConfigs, infoId, value.name, true);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const processParameter = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: t('流程参数'),
|
||||
id: 'processParameter',
|
||||
disabled: true,
|
||||
children: getProcessParamConfigs(),
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
174
src/views/workflow/design/bpmn/components/parameters/Process.vue
Normal file
174
src/views/workflow/design/bpmn/components/parameters/Process.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<div class="list-box">
|
||||
<div class="opr-box">
|
||||
<NodeHead :nodeName="t('流程参数')" />
|
||||
<div class="button-box">
|
||||
<a-button type="primary" @click="add">{{ t('添加参数') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<a-table
|
||||
:dataSource="processInfo.processParamConfigs"
|
||||
:columns="configColumns"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'name'">
|
||||
<a-input v-model:value="record.name" :placeholder="t('参数名称')" />
|
||||
</template>
|
||||
<template v-if="column.key === 'type'">
|
||||
<a-select
|
||||
v-model:value="record.type"
|
||||
:placeholder="t('请选择类型')"
|
||||
style="width: 80px"
|
||||
@change="
|
||||
() => {
|
||||
record.value = null;
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-select-option v-for="item in typeOptions" :key="item.id" :value="item.id">
|
||||
{{ item.name }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template v-if="column.key === 'value'">
|
||||
<a-input
|
||||
v-if="record.type == OperationType.VALUE"
|
||||
v-model:value="record.value"
|
||||
:placeholder="t('请填写参数值')"
|
||||
/>
|
||||
<a-tree-select
|
||||
v-if="record.type == OperationType.VARIABLE"
|
||||
v-model:value="record.value"
|
||||
autoExpandParent
|
||||
treeDefaultExpandAll
|
||||
:tree-data="variableTree"
|
||||
style="width: 100%"
|
||||
:placeholder="t('请选择参数值')"
|
||||
:field-names="{
|
||||
children: 'children',
|
||||
label: 'title',
|
||||
value: 'key',
|
||||
}"
|
||||
/>
|
||||
<ScriptApiSelect
|
||||
style="width: 100%"
|
||||
v-if="record.type == OperationType.API"
|
||||
v-model="record.apiConfig"
|
||||
:need-hide-components="true"
|
||||
/>
|
||||
</template>
|
||||
<template v-if="column.key === 'operation'">
|
||||
<a-popconfirm @confirm="deleteItem(index)">
|
||||
<template #title>
|
||||
<p>{{ t('删除参数') }}</p>
|
||||
<p>{{ t('删除流程参数会清空已引用该参数的所有配置,请确认是否继续?') }}</p>
|
||||
<p class="pop-desc">{{
|
||||
t('如果引用该流程参数的配置较多,清空时间会相应变长,请耐心等待。')
|
||||
}}</p>
|
||||
</template>
|
||||
<Icon icon="ant-design:delete-outlined" class="delete-icon" />
|
||||
</a-popconfirm>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
//import ScriptApiSelect from '/@bpmn/components/arguments/ApiSelect.vue'; //之前是调的这个,现在换成ScriptApiSelect.vue
|
||||
import ScriptApiSelect from '/@bpmn/components/arguments/ScriptApiSelect.vue';
|
||||
import { ProcessArgumentTreeData } from '/@bpmn/config/rules';
|
||||
import { OperationType } from '/@/enums/workflowEnum';
|
||||
import { useBpmnStore } from '/@bpmn/store/bpmn';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { randomNum } from '/@bpmn/util/random';
|
||||
import { updateProcessParameterRelevance } from '/@bpmn/config/useUpdateAllFormInfo';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const configColumns = [
|
||||
{
|
||||
title: t('参数名称'),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
title: t('类型'),
|
||||
dataIndex: 'type',
|
||||
key: 'type',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: t('参数值'),
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
dataIndex: 'operation',
|
||||
key: 'operation',
|
||||
width: 60,
|
||||
},
|
||||
];
|
||||
const store = useBpmnStore();
|
||||
const { processInfo } = storeToRefs(store);
|
||||
let typeOptions = [
|
||||
{
|
||||
id: OperationType.VALUE,
|
||||
name: t('值'),
|
||||
},
|
||||
{
|
||||
id: OperationType.VARIABLE,
|
||||
name: t('变量'),
|
||||
},
|
||||
{
|
||||
id: OperationType.API,
|
||||
name: 'API',
|
||||
},
|
||||
];
|
||||
let variableTree = ProcessArgumentTreeData;
|
||||
// 新增
|
||||
function add() {
|
||||
processInfo.value.processParamConfigs.push({
|
||||
id: randomNum(),
|
||||
name: '',
|
||||
type: OperationType.VALUE,
|
||||
value: '',
|
||||
apiConfig: {
|
||||
id: '',
|
||||
name: '',
|
||||
method: '',
|
||||
requestParamsConfigs: [], //Query Params
|
||||
requestHeaderConfigs: [], //Header
|
||||
requestBodyConfigs: [], //Body
|
||||
},
|
||||
});
|
||||
}
|
||||
//删除
|
||||
function deleteItem(index: number) {
|
||||
updateProcessParameterRelevance(processInfo.value.processParamConfigs[index].id);
|
||||
processInfo.value.processParamConfigs.splice(index, 1);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-box {
|
||||
.opr-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
color: @clear-color;
|
||||
}
|
||||
|
||||
.pop-desc {
|
||||
font-size: 12px;
|
||||
color: rgb(0 0 0 / 40%);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user