Pre Merge pull request !115 from Jason/dev
commit
eda64e1c84
|
@ -0,0 +1,284 @@
|
|||
<script setup lang="ts">
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
import type { SystemDeptApi } from '#/api/system/dept';
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import { inject, ref } from 'vue';
|
||||
|
||||
import { useVbenDrawer } from '@vben/common-ui';
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
|
||||
import {
|
||||
Divider,
|
||||
Input,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
TabPane,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
TypographyText,
|
||||
} from 'ant-design-vue';
|
||||
|
||||
import { BpmModelFormType } from '#/utils';
|
||||
|
||||
import {
|
||||
FieldPermissionType,
|
||||
NodeType,
|
||||
START_USER_BUTTON_SETTING,
|
||||
} from '../../consts';
|
||||
import {
|
||||
useFormFieldsPermission,
|
||||
useNodeName,
|
||||
useWatchNode,
|
||||
} from '../../helpers';
|
||||
|
||||
defineOptions({ name: 'StartUserNodeConfig' });
|
||||
const props = defineProps({
|
||||
flowNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
// 可发起流程的用户编号
|
||||
const startUserIds = inject<Ref<any[]>>('startUserIds');
|
||||
// 可发起流程的部门编号
|
||||
const startDeptIds = inject<Ref<any[]>>('startDeptIds');
|
||||
// 用户列表
|
||||
const userOptions = inject<Ref<SystemUserApi.User[]>>('userList');
|
||||
// 部门列表
|
||||
const deptOptions = inject<Ref<SystemDeptApi.Dept[]>>('deptList');
|
||||
// 当前节点
|
||||
const currentNode = useWatchNode(props);
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(
|
||||
NodeType.COPY_TASK_NODE,
|
||||
);
|
||||
// 激活的 Tab 标签页
|
||||
const activeTabName = ref('user');
|
||||
// 表单字段权限配置
|
||||
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } =
|
||||
useFormFieldsPermission(FieldPermissionType.WRITE);
|
||||
const getUserNicknames = (userIds: number[]): string => {
|
||||
if (!userIds || userIds.length === 0) {
|
||||
return '';
|
||||
}
|
||||
const nicknames: string[] = [];
|
||||
userIds.forEach((userId) => {
|
||||
const found = userOptions?.value.find((item) => item.id === userId);
|
||||
if (found && found.nickname) {
|
||||
nicknames.push(found.nickname);
|
||||
}
|
||||
});
|
||||
return nicknames.join(',');
|
||||
};
|
||||
|
||||
const getDeptNames = (deptIds: number[]): string => {
|
||||
if (!deptIds || deptIds.length === 0) {
|
||||
return '';
|
||||
}
|
||||
const deptNames: string[] = [];
|
||||
deptIds.forEach((deptId) => {
|
||||
const found = deptOptions?.value.find((item) => item.id === deptId);
|
||||
if (found && found.name) {
|
||||
deptNames.push(found.name);
|
||||
}
|
||||
});
|
||||
return deptNames.join(',');
|
||||
};
|
||||
|
||||
// 使用 VbenDrawer
|
||||
const [Drawer, drawerApi] = useVbenDrawer({
|
||||
header: false,
|
||||
closable: false,
|
||||
onCancel() {
|
||||
drawerApi.close();
|
||||
},
|
||||
onConfirm() {
|
||||
saveConfig();
|
||||
},
|
||||
});
|
||||
|
||||
// 保存配置
|
||||
const saveConfig = async () => {
|
||||
activeTabName.value = 'user';
|
||||
currentNode.value.name = nodeName.value!;
|
||||
currentNode.value.showText = '已设置';
|
||||
// 设置表单权限
|
||||
currentNode.value.fieldsPermission = fieldsPermissionConfig.value;
|
||||
// 设置发起人的按钮权限
|
||||
currentNode.value.buttonsSetting = START_USER_BUTTON_SETTING;
|
||||
drawerApi.close();
|
||||
return true;
|
||||
};
|
||||
|
||||
// 显示发起人节点配置,由父组件传过来
|
||||
const showStartUserNodeConfig = (node: SimpleFlowNode) => {
|
||||
nodeName.value = node.name;
|
||||
// 表单字段权限
|
||||
getNodeConfigFormFields(node.fieldsPermission);
|
||||
drawerApi.open();
|
||||
};
|
||||
|
||||
/** 批量更新权限 */
|
||||
const updatePermission = (type: string) => {
|
||||
fieldsPermissionConfig.value.forEach((field) => {
|
||||
if (type === 'READ') {
|
||||
field.permission = FieldPermissionType.READ;
|
||||
} else if (type === 'WRITE') {
|
||||
field.permission = FieldPermissionType.WRITE;
|
||||
} else {
|
||||
field.permission = FieldPermissionType.NONE;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 暴露方法给父组件
|
||||
*/
|
||||
defineExpose({ showStartUserNodeConfig });
|
||||
</script>
|
||||
<template>
|
||||
<Drawer>
|
||||
<div class="config-header">
|
||||
<!-- TODO v-mountedFocus 自动聚集 需要迁移一下 -->
|
||||
<Input
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
v-model:value="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
<div v-else class="node-name">
|
||||
{{ nodeName }}
|
||||
<IconifyIcon
|
||||
class="ml-1"
|
||||
icon="ep:edit-pen"
|
||||
:size="16"
|
||||
@click="clickIcon()"
|
||||
/>
|
||||
</div>
|
||||
<Divider />
|
||||
</div>
|
||||
|
||||
<Tabs v-model:active-key="activeTabName" type="card">
|
||||
<TabPane tab="权限" key="user">
|
||||
<TypographyText
|
||||
v-if="
|
||||
(!startUserIds || startUserIds.length === 0) &&
|
||||
(!startDeptIds || startDeptIds.length === 0)
|
||||
"
|
||||
>
|
||||
全部成员可以发起流程
|
||||
</TypographyText>
|
||||
<div v-else-if="startUserIds && startUserIds.length > 0">
|
||||
<TypographyText v-if="startUserIds.length === 1">
|
||||
{{ getUserNicknames(startUserIds) }} 可发起流程
|
||||
</TypographyText>
|
||||
<TypographyText v-else>
|
||||
<Tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="getUserNicknames(startUserIds)"
|
||||
>
|
||||
{{ getUserNicknames(startUserIds.slice(0, 2)) }} 等
|
||||
{{ startUserIds.length }} 人可发起流程
|
||||
</Tooltip>
|
||||
</TypographyText>
|
||||
</div>
|
||||
<div v-else-if="startDeptIds && startDeptIds.length > 0">
|
||||
<TypographyText v-if="startDeptIds.length === 1">
|
||||
{{ getDeptNames(startDeptIds) }} 可发起流程
|
||||
</TypographyText>
|
||||
<TypographyText v-else>
|
||||
<Tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="getDeptNames(startDeptIds)"
|
||||
>
|
||||
{{ getDeptNames(startDeptIds.slice(0, 2)) }} 等
|
||||
{{ startDeptIds.length }} 个部门可发起流程
|
||||
</Tooltip>
|
||||
</TypographyText>
|
||||
</div>
|
||||
</TabPane>
|
||||
<TabPane
|
||||
tab="表单字段权限"
|
||||
key="fields"
|
||||
v-if="formType === BpmModelFormType.NORMAL"
|
||||
>
|
||||
<div class="field-setting-pane">
|
||||
<div class="field-setting-desc">字段权限</div>
|
||||
<div class="field-permit-title">
|
||||
<div class="setting-title-label first-title">字段名称</div>
|
||||
<div class="other-titles">
|
||||
<span
|
||||
class="setting-title-label cursor-pointer"
|
||||
@click="updatePermission('READ')"
|
||||
>
|
||||
只读
|
||||
</span>
|
||||
<span
|
||||
class="setting-title-label cursor-pointer"
|
||||
@click="updatePermission('WRITE')"
|
||||
>
|
||||
可编辑
|
||||
</span>
|
||||
<span
|
||||
class="setting-title-label cursor-pointer"
|
||||
@click="updatePermission('NONE')"
|
||||
>
|
||||
隐藏
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="field-setting-item"
|
||||
v-for="(item, index) in fieldsPermissionConfig"
|
||||
:key="index"
|
||||
>
|
||||
<div class="field-setting-item-label">{{ item.title }}</div>
|
||||
<RadioGroup
|
||||
class="field-setting-item-group"
|
||||
v-model:value="item.permission"
|
||||
>
|
||||
<div class="item-radio-wrap">
|
||||
<Radio
|
||||
:value="FieldPermissionType.READ"
|
||||
size="large"
|
||||
:label="FieldPermissionType.READ"
|
||||
>
|
||||
<span></span>
|
||||
</Radio>
|
||||
</div>
|
||||
<div class="item-radio-wrap">
|
||||
<Radio
|
||||
:value="FieldPermissionType.WRITE"
|
||||
size="large"
|
||||
:label="FieldPermissionType.WRITE"
|
||||
>
|
||||
<span></span>
|
||||
</Radio>
|
||||
</div>
|
||||
<div class="item-radio-wrap">
|
||||
<Radio
|
||||
:value="FieldPermissionType.NONE"
|
||||
size="large"
|
||||
:label="FieldPermissionType.NONE"
|
||||
>
|
||||
<span></span>
|
||||
</Radio>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Drawer>
|
||||
</template>
|
||||
<style lang="scss" scoped></style>
|
|
@ -11,6 +11,7 @@ import { Input } from 'ant-design-vue';
|
|||
|
||||
import { NODE_DEFAULT_TEXT, NodeType } from '../../consts';
|
||||
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
|
||||
import StartUserNodeConfig from '../nodes-config/start-user-node-config.vue';
|
||||
import NodeHandler from './node-handler.vue';
|
||||
|
||||
defineOptions({ name: 'StartUserNode' });
|
||||
|
@ -55,8 +56,7 @@ const nodeClick = () => {
|
|||
'TODO 编辑模式,打开节点配置、把当前节点传递给配置组件',
|
||||
nodeSetting.value,
|
||||
);
|
||||
// nodeSetting.value.showStartUserNodeConfig(currentNode.value);
|
||||
// nodeSetting.value.openDrawer();
|
||||
nodeSetting.value.showStartUserNodeConfig(currentNode.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -108,12 +108,12 @@ const nodeClick = () => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO 发起人配置节点
|
||||
<StartUserNodeConfig
|
||||
|
||||
<StartUserNodeConfig
|
||||
v-if="!readonly && currentNode"
|
||||
ref="nodeSetting"
|
||||
:flow-node="currentNode"
|
||||
/> -->
|
||||
/>
|
||||
<!-- 审批记录 TODO -->
|
||||
</template>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -3,21 +3,19 @@ import type { Ref } from 'vue';
|
|||
|
||||
import type { SimpleFlowNode } from '../consts';
|
||||
|
||||
import type { BpmFormApi } from '#/api/bpm/form';
|
||||
import type { BpmUserGroupApi } from '#/api/bpm/userGroup';
|
||||
import type { SystemDeptApi } from '#/api/system/dept';
|
||||
import type { SystemPostApi } from '#/api/system/post';
|
||||
import type { SystemRoleApi } from '#/api/system/role';
|
||||
import type { SystemUserApi } from '#/api/system/user';
|
||||
|
||||
import { inject, onMounted, provide, ref } from 'vue';
|
||||
import { inject, onMounted, provide, ref, watch } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Button, Modal } from 'ant-design-vue';
|
||||
|
||||
import { getFormDetail } from '#/api/bpm/form';
|
||||
import { getModel } from '#/api/bpm/model';
|
||||
import { getUserGroupSimpleList } from '#/api/bpm/userGroup';
|
||||
import { getSimpleDeptList } from '#/api/system/dept';
|
||||
import { getSimplePostList } from '#/api/system/post';
|
||||
|
@ -33,21 +31,23 @@ defineOptions({
|
|||
});
|
||||
|
||||
const props = defineProps({
|
||||
modelId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
modelKey: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
modelName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
// 流程表单 ID
|
||||
modelFormId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
// 表单类型
|
||||
modelFormType: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: BpmModelFormType.NORMAL,
|
||||
},
|
||||
// 可发起流程的人员编号
|
||||
startUserIds: {
|
||||
type: Array,
|
||||
|
@ -66,7 +66,31 @@ const emits = defineEmits(['success']);
|
|||
const processData = inject('processData') as Ref;
|
||||
const loading = ref(false);
|
||||
const formFields = ref<string[]>([]);
|
||||
const formType = ref(20);
|
||||
const formType = ref(props.modelFormType);
|
||||
|
||||
// 监听 modelFormType 变化
|
||||
watch(
|
||||
() => props.modelFormType,
|
||||
(newVal) => {
|
||||
formType.value = newVal;
|
||||
},
|
||||
);
|
||||
|
||||
// 监听 modelFormId 变化
|
||||
watch(
|
||||
() => props.modelFormId,
|
||||
async (newVal) => {
|
||||
if (newVal) {
|
||||
const form = await getFormDetail(newVal);
|
||||
formFields.value = form?.fields;
|
||||
} else {
|
||||
// 如果 modelFormId 为空,清空表单字段
|
||||
formFields.value = [];
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
const roleOptions = ref<SystemRoleApi.Role[]>([]); // 角色列表
|
||||
const postOptions = ref<SystemPostApi.Post[]>([]); // 岗位列表
|
||||
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
|
||||
|
@ -172,19 +196,6 @@ const validateNode = (
|
|||
onMounted(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
// 获取表单字段
|
||||
if (props.modelId) {
|
||||
const bpmnModel = await getModel(props.modelId);
|
||||
if (bpmnModel) {
|
||||
formType.value = bpmnModel.formType;
|
||||
if (formType.value === BpmModelFormType.NORMAL && bpmnModel.formId) {
|
||||
const bpmnForm = (await getFormDetail(
|
||||
bpmnModel.formId,
|
||||
)) as unknown as BpmFormApi.FormVO;
|
||||
formFields.value = bpmnForm?.fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获得角色列表
|
||||
roleOptions.value = await getSimpleRoleList();
|
||||
// 获得岗位列表
|
||||
|
|
|
@ -198,8 +198,8 @@ onMounted(() => {
|
|||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="simple-process-model-container position-relative h-full">
|
||||
<div class="z-index-button-group absolute right-[0px] top-[0px] bg-[#fff]">
|
||||
<div class="simple-process-model-container">
|
||||
<div class="absolute right-[0px] top-[0px] bg-[#fff]">
|
||||
<Row type="flex" justify="end">
|
||||
<ButtonGroup key="scale-control">
|
||||
<Button v-if="!readonly" @click="exportJson">
|
||||
|
@ -247,6 +247,7 @@ onMounted(() => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- TODO 这个好像暂时没有用到。保存失败弹窗 -->
|
||||
<Modal
|
||||
v-model:open="errorDialogVisible"
|
||||
title="保存失败"
|
||||
|
@ -266,21 +267,4 @@ onMounted(() => {
|
|||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.simple-process-model-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
user-select: none; // 禁用文本选择
|
||||
}
|
||||
|
||||
.simple-process-model {
|
||||
position: relative; // 确保相对定位
|
||||
min-width: 100%; // 确保宽度为100%
|
||||
min-height: 100%; // 确保高度为100%
|
||||
}
|
||||
|
||||
.z-index-ButtonGroup {
|
||||
z-index: 10;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -209,8 +209,8 @@
|
|||
.simple-process-model-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-top: 32px;
|
||||
overflow-x: auto;
|
||||
background: url('./svg/simple-process-bg.svg') 0 0 repeat;
|
||||
background-color: #fafafa;
|
||||
|
||||
.simple-process-model {
|
||||
|
@ -219,6 +219,7 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: fit-content;
|
||||
background: url('./svg/simple-process-bg.svg') 0 0 repeat;
|
||||
transform: scale(1);
|
||||
transform-origin: 50% 0 0;
|
||||
transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from '#/api/bpm/model';
|
||||
import { getSimpleDeptList } from '#/api/system/dept';
|
||||
import { getSimpleUserList } from '#/api/system/user';
|
||||
import { BpmAutoApproveType, BpmModelFormType, BpmModelType } from '#/utils';
|
||||
|
||||
import BasicInfo from './modules/basic-info.vue';
|
||||
import FormDesign from './modules/form-design.vue';
|
||||
|
@ -33,24 +34,6 @@ import ProcessDesign from './modules/process-design.vue';
|
|||
|
||||
defineOptions({ name: 'BpmModelCreate' });
|
||||
|
||||
// TODO 这个常量是不是所有 apps 都可以使用, 放 @utils/constant.ts 不能共享, @芋艿 这些常量放哪里合适!
|
||||
// TODO @jason:/Users/yunai/Java/yudao-ui-admin-vben-v5/apps/web-antd/src/utils/constants.ts;先不多个 apps 共享哈;
|
||||
const BpmModelType = {
|
||||
BPMN: 10, // BPMN 设计器
|
||||
SIMPLE: 20, // 简易设计器
|
||||
};
|
||||
|
||||
const BpmModelFormType = {
|
||||
NORMAL: 10, // 流程表单
|
||||
CUSTOM: 20, // 业务表单
|
||||
};
|
||||
|
||||
const BpmAutoApproveType = {
|
||||
NONE: 0, // 不自动通过
|
||||
APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过
|
||||
APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过
|
||||
};
|
||||
|
||||
// 流程定义类型
|
||||
type BpmProcessDefinitionType = Omit<
|
||||
BpmProcessDefinitionApi.ProcessDefinitionVO,
|
||||
|
|
|
@ -55,9 +55,9 @@ defineExpose({
|
|||
<template v-else>
|
||||
<SimpleModelDesign
|
||||
v-if="showDesigner"
|
||||
:model-id="modelData.id"
|
||||
:model-key="modelData.key"
|
||||
:model-name="modelData.name"
|
||||
:model-form-id="modelData.formId"
|
||||
:model-form-type="modelData.formType"
|
||||
:start-user-ids="modelData.startUserIds"
|
||||
:start-dept-ids="modelData.startDeptIds"
|
||||
@success="handleDesignSuccess"
|
||||
|
|
|
@ -7,8 +7,8 @@ import { SimpleProcessDesigner } from '#/components/simple-process-design';
|
|||
defineOptions({ name: 'SimpleModelDesign' });
|
||||
|
||||
defineProps<{
|
||||
modelId?: string;
|
||||
modelKey?: string;
|
||||
modelFormId?: number;
|
||||
modelFormType?: number;
|
||||
modelName?: string;
|
||||
startDeptIds?: number[];
|
||||
startUserIds?: number[];
|
||||
|
@ -27,9 +27,9 @@ const handleSuccess = (data?: any) => {
|
|||
<template>
|
||||
<ContentWrap :body-style="{ padding: '20px 16px' }">
|
||||
<SimpleProcessDesigner
|
||||
:model-id="modelId"
|
||||
:model-key="modelKey"
|
||||
:model-form-id="modelFormId"
|
||||
:model-name="modelName"
|
||||
:model-form-type="modelFormType"
|
||||
@success="handleSuccess"
|
||||
:start-user-ids="startUserIds"
|
||||
:start-dept-ids="startDeptIds"
|
||||
|
|
|
@ -162,7 +162,7 @@ const handleCategorySortSubmit = async () => {
|
|||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<!-- TODO @jaosn:没头像的图标,展示文字头像哈 -->
|
||||
<!-- TODO @jaosn:没头像的图标,展示文字头像哈 @芋艿 好像已经展示了文字头像。是模型列表中吗? -->
|
||||
<!-- 流程分类表单弹窗 -->
|
||||
<CategoryFormModal @success="getList" />
|
||||
<Card
|
||||
|
|
Loading…
Reference in New Issue