Pre Merge pull request !117 from Jason/dev

pull/117/MERGE
Jason 2025-05-27 14:15:43 +00:00 committed by Gitee
commit fbfe1a01be
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
6 changed files with 1538 additions and 171 deletions

View File

@ -12,10 +12,11 @@ import { useVbenDrawer } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import {
Divider,
Col,
Input,
Radio,
RadioGroup,
Row,
TabPane,
Tabs,
Tooltip,
@ -91,8 +92,8 @@ const getDeptNames = (deptIds: number[]): string => {
// 使 VbenDrawer
const [Drawer, drawerApi] = useVbenDrawer({
header: false,
closable: false,
header: true,
closable: true,
onCancel() {
drawerApi.close();
},
@ -142,28 +143,28 @@ 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()"
<template #title>
<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>
</div>
<Divider />
</div>
</template>
<Tabs v-model:active-key="activeTabName" type="card">
<TabPane tab="权限" key="user">
<TypographyText
@ -212,69 +213,76 @@ defineExpose({ showStartUserNodeConfig });
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 class="p-1">
<div class="mb-4 text-[16px] font-bold">字段权限</div>
<!-- 表头 -->
<Row class="border border-gray-200 px-4 py-3">
<Col :span="8" class="font-bold">字段名称</Col>
<Col :span="16">
<Row>
<Col :span="8" class="flex items-center justify-center">
<span
class="cursor-pointer font-bold"
@click="updatePermission('READ')"
>
只读
</span>
</Col>
<Col :span="8" class="flex items-center justify-center">
<span
class="cursor-pointer font-bold"
@click="updatePermission('WRITE')"
>
可编辑
</span>
</Col>
<Col :span="8" class="flex items-center justify-center">
<span
class="cursor-pointer font-bold"
@click="updatePermission('NONE')"
>
隐藏
</span>
</Col>
</Row>
</Col>
</Row>
<!-- 表格内容 -->
<div v-for="(item, index) in fieldsPermissionConfig" :key="index">
<Row class="border border-t-0 border-gray-200 px-4 py-2">
<Col :span="8" class="flex items-center truncate">
{{ item.title }}
</Col>
<Col :span="16">
<RadioGroup v-model:value="item.permission" class="w-full">
<Row>
<Col :span="8" class="flex items-center justify-center">
<Radio
:value="FieldPermissionType.READ"
size="large"
:label="FieldPermissionType.READ"
/>
</Col>
<Col :span="8" class="flex items-center justify-center">
<Radio
:value="FieldPermissionType.WRITE"
size="large"
:label="FieldPermissionType.WRITE"
/>
</Col>
<Col :span="8" class="flex items-center justify-center">
<Radio
:value="FieldPermissionType.NONE"
size="large"
:label="FieldPermissionType.NONE"
/>
</Col>
</Row>
</RadioGroup>
</Col>
</Row>
</div>
</div>
</TabPane>

View File

@ -0,0 +1,48 @@
import { APPROVE_TYPE, ApproveType, TimeUnitType } from '../../consts';
/** 获取条件节点默认的名称 */
export const getDefaultConditionNodeName = (
index: number,
defaultFlow: boolean | undefined,
): string => {
if (defaultFlow) {
return '其它情况';
}
return `条件${index + 1}`;
};
/** 获取包容分支条件节点默认的名称 */
export const getDefaultInclusiveConditionNodeName = (
index: number,
defaultFlow: boolean | undefined,
): string => {
if (defaultFlow) {
return '其它情况';
}
return `包容条件${index + 1}`;
};
/** 转换时间单位字符串为枚举值 */
export const convertTimeUnit = (strTimeUnit: string) => {
if (strTimeUnit === 'M') {
return TimeUnitType.MINUTE;
}
if (strTimeUnit === 'H') {
return TimeUnitType.HOUR;
}
if (strTimeUnit === 'D') {
return TimeUnitType.DAY;
}
return TimeUnitType.HOUR;
};
/** 根据审批类型获取对应的文本描述 */
export const getApproveTypeText = (approveType: ApproveType): string => {
let approveTypeText = '';
APPROVE_TYPE.forEach((item) => {
if (item.value === approveType) {
approveTypeText = item.label;
}
});
return approveTypeText;
};

View File

@ -0,0 +1,138 @@
<script setup lang="ts">
import type { Ref } from 'vue';
import type { SimpleFlowNode } from '../../consts';
import { inject, ref } from 'vue';
import { IconifyIcon } from '@vben/icons';
import { Input } from 'ant-design-vue';
import { NODE_DEFAULT_TEXT, NodeType } from '../../consts';
import { useNodeName2, useTaskStatusClass, useWatchNode } from '../../helpers';
import UserTaskNodeConfig from '../nodes-config/user-task-node-config.vue';
import NodeHandler from './node-handler.vue';
defineOptions({ name: 'UserTaskNode' });
const props = defineProps({
flowNode: {
type: Object as () => SimpleFlowNode,
required: true,
},
});
const emits = defineEmits<{
findParentNode: [nodeList: SimpleFlowNode[], nodeType: NodeType];
'update:flowNode': [node: SimpleFlowNode | undefined];
}>();
//
const readonly = inject<Boolean>('readonly');
const tasks = inject<Ref<any[]>>('tasks', ref([]));
//
const currentNode = useWatchNode(props);
//
const { showInput, blurEvent, clickTitle } = useNodeName2(
currentNode,
NodeType.START_USER_NODE,
);
const nodeSetting = ref();
const nodeClick = () => {
if (readonly) {
if (tasks && tasks.value) {
// TODO
console.warn('只读模式,弹窗显示任务信息待实现');
}
} else {
//
nodeSetting.value.showUserTaskNodeConfig(currentNode.value);
}
};
const deleteNode = () => {
emits('update:flowNode', currentNode.value.childNode);
};
//
const findReturnTaskNodes = (
matchNodeList: SimpleFlowNode[], //
) => {
//
emits('findParentNode', matchNodeList, NodeType.USER_TASK_NODE);
};
// const selectTasks = ref<any[] | undefined>([]); //
</script>
<template>
<div class="node-wrapper">
<div class="node-container">
<div
class="node-box"
:class="[
{ 'node-config-error': !currentNode.showText },
`${useTaskStatusClass(currentNode?.activityStatus)}`,
]"
>
<div class="node-title-container">
<div
:class="`node-title-icon ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'transactor-task' : 'user-task'}`"
>
<span
:class="`iconfont ${currentNode.type === NodeType.TRANSACTOR_NODE ? 'icon-transactor' : 'icon-approve'}`"
>
</span>
</div>
<Input
v-if="!readonly && showInput"
type="text"
class="editable-title-input"
@blur="blurEvent()"
v-model="currentNode.name"
:placeholder="currentNode.name"
/>
<div v-else class="node-title" @click="clickTitle">
{{ currentNode.name }}
</div>
</div>
<div class="node-content" @click="nodeClick">
<div
class="node-text"
:title="currentNode.showText"
v-if="currentNode.showText"
>
{{ currentNode.showText }}
</div>
<div class="node-text" v-else>
{{ NODE_DEFAULT_TEXT.get(currentNode.type) }}
</div>
<IconifyIcon icon="ep:arrow-right-bold" v-if="!readonly" />
</div>
<div v-if="!readonly" class="node-toolbar">
<div class="toolbar-icon">
<IconifyIcon
color="#0089ff"
icon="ep:circle-close-filled"
:size="18"
@click="deleteNode"
/>
</div>
</div>
</div>
<!-- 传递子节点给添加节点组件会在子节点前面添加节点 -->
<NodeHandler
v-if="currentNode"
v-model:child-node="currentNode.childNode"
:current-node="currentNode"
/>
</div>
</div>
<UserTaskNodeConfig
v-if="currentNode"
ref="nodeSetting"
:flow-node="currentNode"
@find-return-task-nodes="findReturnTaskNodes"
/>
<!-- TODO 审批记录 -->
</template>
<style lang="scss" scoped></style>

View File

@ -5,6 +5,7 @@ import { NodeType } from '../consts';
import { useWatchNode } from '../helpers';
import EndEventNode from './nodes/end-event-node.vue';
import StartUserNode from './nodes/start-user-node.vue';
import UserTaskNode from './nodes/user-task-node.vue';
defineOptions({ name: 'ProcessNodeTree' });
const props = defineProps({
@ -29,16 +30,12 @@ const emits = defineEmits<{
const currentNode = useWatchNode(props);
//
// eslint-disable-next-line unused-imports/no-unused-vars, no-unused-vars
const handleModelValueUpdate = (updateValue: any) => {
emits('update:flowNode', updateValue);
};
// eslint-disable-next-line unused-imports/no-unused-vars, no-unused-vars
const triggerFromParentNode = (
nodeList: SimpleFlowNode[],
nodeType: number,
) => {
const findParentNode = (nodeList: SimpleFlowNode[], nodeType: number) => {
emits('recursiveFindParentNode', nodeList, props.parentNode, nodeType);
};
@ -69,7 +66,7 @@ const recursiveFindParentNode = (
:flow-node="currentNode"
/>
<!-- 审批节点 -->
<!-- <UserTaskNode
<UserTaskNode
v-if="
currentNode &&
(currentNode.type === NodeType.USER_TASK_NODE ||
@ -77,8 +74,8 @@ const recursiveFindParentNode = (
"
:flow-node="currentNode"
@update:flow-node="handleModelValueUpdate"
@find:parent-node="findFromParentNode"
/> -->
@find-parent-node="findParentNode"
/>
<!-- 抄送节点 -->
<!-- <CopyTaskNode
v-if="currentNode && currentNode.type === NodeType.COPY_TASK_NODE"

View File

@ -44,82 +44,6 @@
}
}
}
//
.field-setting-pane {
display: flex;
flex-direction: column;
font-size: 14px;
.field-setting-desc {
padding-right: 8px;
margin-bottom: 16px;
font-size: 16px;
font-weight: 700;
}
.field-permit-title {
display: flex;
align-items: center;
justify-content: space-between;
height: 45px;
padding-left: 12px;
line-height: 45px;
background-color: #f8fafc0a;
border: 1px solid #1f38581a;
.first-title {
text-align: left !important;
}
.other-titles {
display: flex;
justify-content: space-between;
}
.setting-title-label {
display: inline-block;
width: 110px;
padding: 5px 0;
font-size: 13px;
font-weight: 700;
color: #000;
text-align: center;
}
}
.field-setting-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 38px;
padding-left: 12px;
border: 1px solid #1f38581a;
border-top: 0;
.field-setting-item-label {
display: inline-block;
width: 110px;
min-height: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: text;
}
.field-setting-item-group {
display: flex;
justify-content: space-between;
.item-radio-wrap {
display: inline-block;
width: 110px;
text-align: center;
}
}
}
}
// 线
.handler-item-wrapper {
display: flex;