Pre Merge pull request !138 from Jason/dev

pull/138/MERGE
Jason 2025-06-11 13:22:12 +00:00 committed by Gitee
commit 24643b4099
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
17 changed files with 201 additions and 74 deletions

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { ref, watch } from 'vue'; import { nextTick, ref, watch } from 'vue';
import { useVbenDrawer } from '@vben/common-ui'; import { useVbenDrawer } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
@ -53,8 +53,6 @@ const condition = ref<any>({
}, },
}); });
//
const showInput = ref(false);
const conditionRef = ref(); const conditionRef = ref();
const fieldOptions = useFormFieldsAndStartUser(); // const fieldOptions = useFormFieldsAndStartUser(); //
@ -130,13 +128,24 @@ watch(
currentNode.value = newValue; currentNode.value = newValue;
}, },
); );
//
const showInput = ref(false);
//
const inputRef = ref<HTMLInputElement | null>(null);
// showInput true
watch(showInput, (value) => {
if (value) {
nextTick(() => {
inputRef.value?.focus();
});
}
});
function clickIcon() { function clickIcon() {
showInput.value = true; showInput.value = true;
} }
// //
function blurEvent() { function changeNodeName() {
showInput.value = false; showInput.value = false;
currentNode.value.name = currentNode.value.name =
currentNode.value.name || currentNode.value.name ||
@ -153,10 +162,12 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
<template #title> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="currentNode.name" v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />

View File

@ -75,9 +75,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.COPY_TASK_NODE, useNodeName(BpmNodeTypeEnum.COPY_TASK_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -213,9 +212,11 @@ defineExpose({ showCopyTaskNodeConfig }); // 暴露方法给父组件
<div class="config-header"> <div class="config-header">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -45,9 +45,8 @@ const props = defineProps({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.DELAY_TIMER_NODE, useNodeName(BpmNodeTypeEnum.DELAY_TIMER_NODE);
);
// //
const formRef = ref(); // Ref const formRef = ref(); // Ref
@ -158,9 +157,11 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -41,9 +41,8 @@ const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree');
/** 当前节点 */ /** 当前节点 */
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
/** 节点名称 */ /** 节点名称 */
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.ROUTER_BRANCH_NODE, useNodeName(BpmNodeTypeEnum.ROUTER_BRANCH_NODE);
);
const routerGroups = ref<RouterSetting[]>([]); const routerGroups = ref<RouterSetting[]>([]);
const nodeOptions = ref<any[]>([]); const nodeOptions = ref<any[]>([]);
const conditionRef = ref<any[]>([]); const conditionRef = ref<any[]>([]);
@ -205,10 +204,12 @@ defineExpose({ openDrawer }); // 暴露方法给父组件
<template #title> <template #title>
<div class="flex items-center"> <div class="flex items-center">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="mr-2 w-48" class="mr-2 w-48"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -52,9 +52,8 @@ const deptOptions = inject<Ref<SystemDeptApi.Dept[]>>('deptList');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.START_USER_NODE, useNodeName(BpmNodeTypeEnum.START_USER_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -145,12 +144,13 @@ defineExpose({ showStartUserNodeConfig });
<Drawer> <Drawer>
<template #title> <template #title>
<div class="config-header"> <div class="config-header">
<!-- TODO v-mountedFocus 自动聚集 需要迁移一下 -->
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -72,9 +72,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.TRIGGER_NODE, useNodeName(BpmNodeTypeEnum.TRIGGER_NODE);
);
// //
const formRef = ref(); // Ref const formRef = ref(); // Ref
@ -388,10 +387,12 @@ onMounted(() => {
<template #title> <template #title>
<div class="config-header"> <div class="config-header">
<Input <Input
ref="inputRef"
v-if="showInput" v-if="showInput"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -114,9 +114,8 @@ const [Drawer, drawerApi] = useVbenDrawer({
}); });
// //
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName( const { nodeName, showInput, clickIcon, changeNodeName, inputRef } =
BpmNodeTypeEnum.USER_TASK_NODE, useNodeName(BpmNodeTypeEnum.USER_TASK_NODE);
);
// Tab // Tab
const activeTabName = ref('user'); const activeTabName = ref('user');
@ -586,9 +585,11 @@ onMounted(() => {
<div class="config-header"> <div class="config-header">
<Input <Input
v-if="showInput" v-if="showInput"
ref="inputRef"
type="text" type="text"
class="config-editable-input" class="config-editable-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="nodeName" v-model:value="nodeName"
:placeholder="nodeName" :placeholder="nodeName"
/> />

View File

@ -32,7 +32,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.COPY_TASK_NODE, BpmNodeTypeEnum.COPY_TASK_NODE,
); );
@ -67,11 +67,13 @@ function deleteNode() {
<span class="iconfont icon-copy"></span> <span class="iconfont icon-copy"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -30,7 +30,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.DELAY_TIMER_NODE, BpmNodeTypeEnum.DELAY_TIMER_NODE,
); );
@ -64,11 +64,13 @@ function deleteNode() {
<span class="iconfont icon-delay"></span> <span class="iconfont icon-delay"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { getCurrentInstance, inject, ref, watch } from 'vue'; import { getCurrentInstance, inject, nextTick, ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils'; import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
@ -51,11 +51,30 @@ watch(
currentNode.value = newValue; currentNode.value = newValue;
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); const showInputs = ref<boolean[]>([]);
// //
function blurEvent(index: number) { watch(
showInputs,
(newValues) => {
// true ,
newValues.forEach((value, index) => {
if (value) {
// false true ,
nextTick(() => {
inputRefs.value[index]?.focus();
});
}
});
},
{ deep: true },
);
//
function changeNodeName(index: number) {
showInputs.value[index] = false; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -188,10 +207,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]"> <div v-if="!readonly && showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { getCurrentInstance, inject, ref, watch } from 'vue'; import { getCurrentInstance, inject, nextTick, ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils'; import { cloneDeep, buildShortUUID as generateUUID } from '@vben/utils';
@ -57,10 +57,28 @@ watch(
currentNode.value = newValue; currentNode.value = newValue;
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); const showInputs = ref<boolean[]>([]);
// //
function blurEvent(index: number) { watch(
showInputs,
(newValues) => {
// true ,
newValues.forEach((value, index) => {
if (value) {
// false true ,
nextTick(() => {
inputRefs.value[index]?.focus();
});
}
});
},
{ deep: true },
);
//
function changeNodeName(index: number) {
showInputs.value[index] = false; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -192,10 +210,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="!readonly && showInputs[index]"> <div v-if="!readonly && showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SimpleFlowNode } from '../../consts'; import type { SimpleFlowNode } from '../../consts';
import { inject, ref, watch } from 'vue'; import { inject, nextTick, ref, watch } from 'vue';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { buildShortUUID as generateUUID } from '@vben/utils'; import { buildShortUUID as generateUUID } from '@vben/utils';
@ -46,10 +46,28 @@ watch(
}, },
); );
//
const inputRefs = ref<HTMLInputElement[]>([]);
//
const showInputs = ref<boolean[]>([]); const showInputs = ref<boolean[]>([]);
//
// watch(
function blurEvent(index: number) { showInputs,
(newValues) => {
// ,
newValues.forEach((value, index) => {
if (value) {
// false true ,
nextTick(() => {
inputRefs.value[index]?.focus();
});
}
});
},
{ deep: true },
);
//
function changeNodeName(index: number) {
showInputs.value[index] = false; showInputs.value[index] = false;
const conditionNode = currentNode.value.conditionNodes?.at( const conditionNode = currentNode.value.conditionNodes?.at(
index, index,
@ -150,10 +168,16 @@ function recursiveFindParentNode(
<div class="branch-node-title-container"> <div class="branch-node-title-container">
<div v-if="showInputs[index]"> <div v-if="showInputs[index]">
<Input <Input
:ref="
(el) => {
inputRefs[index] = el as HTMLInputElement;
}
"
type="text" type="text"
class="input-max-width editable-title-input" class="input-max-width editable-title-input"
@blur="blurEvent(index)" @blur="changeNodeName(index)"
v-model="item.name" @press-enter="changeNodeName(index)"
v-model:value="item.name"
/> />
</div> </div>
<div v-else class="branch-title" @click="clickEvent(index)"> <div v-else class="branch-title" @click="clickEvent(index)">

View File

@ -33,7 +33,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.ROUTER_BRANCH_NODE, BpmNodeTypeEnum.ROUTER_BRANCH_NODE,
); );
@ -67,11 +67,13 @@ function deleteNode() {
<span class="iconfont icon-router"></span> <span class="iconfont icon-router"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -37,7 +37,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.START_USER_NODE, BpmNodeTypeEnum.START_USER_NODE,
); );
@ -81,10 +81,12 @@ function nodeClick() {
<span class="iconfont icon-start-user"></span> <span class="iconfont icon-start-user"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
@press-enter="changeNodeName()"
v-model:value="currentNode.name" v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />

View File

@ -35,7 +35,7 @@ const readonly = inject<Boolean>('readonly');
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.TRIGGER_NODE, BpmNodeTypeEnum.TRIGGER_NODE,
); );
@ -69,11 +69,13 @@ function deleteNode() {
<span class="iconfont icon-trigger"></span> <span class="iconfont icon-trigger"></span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -36,7 +36,7 @@ const tasks = inject<Ref<any[]>>('tasks', ref([]));
// //
const currentNode = useWatchNode(props); const currentNode = useWatchNode(props);
// //
const { showInput, blurEvent, clickTitle } = useNodeName2( const { showInput, changeNodeName, clickTitle, inputRef } = useNodeName2(
currentNode, currentNode,
BpmNodeTypeEnum.USER_TASK_NODE, BpmNodeTypeEnum.USER_TASK_NODE,
); );
@ -87,11 +87,13 @@ function findReturnTaskNodes(
</span> </span>
</div> </div>
<Input <Input
ref="inputRef"
v-if="!readonly && showInput" v-if="!readonly && showInput"
type="text" type="text"
class="editable-title-input" class="editable-title-input"
@blur="blurEvent()" @blur="changeNodeName()"
v-model="currentNode.name" @press-enter="changeNodeName()"
v-model:value="currentNode.name"
:placeholder="currentNode.name" :placeholder="currentNode.name"
/> />
<div v-else class="node-title" @click="clickTitle"> <div v-else class="node-title" @click="clickTitle">

View File

@ -12,7 +12,7 @@ import type { SystemPostApi } from '#/api/system/post';
import type { SystemRoleApi } from '#/api/system/role'; import type { SystemRoleApi } from '#/api/system/role';
import type { SystemUserApi } from '#/api/system/user'; import type { SystemUserApi } from '#/api/system/user';
import { inject, ref, toRaw, unref, watch } from 'vue'; import { inject, nextTick, ref, toRaw, unref, watch } from 'vue';
import { import {
BpmNodeTypeEnum, BpmNodeTypeEnum,
@ -622,21 +622,33 @@ export function useNodeName(nodeType: BpmNodeTypeEnum) {
const nodeName = ref<string>(); const nodeName = ref<string>();
// 节点名称输入框 // 节点名称输入框
const showInput = ref(false); const showInput = ref(false);
// 输入框的引用
const inputRef = ref<HTMLInputElement | null>(null);
// 点击节点名称编辑图标 // 点击节点名称编辑图标
function clickIcon() { function clickIcon() {
showInput.value = true; showInput.value = true;
} }
// 节点名称输入框失去焦点 // 修改节点名称
function blurEvent() { function changeNodeName() {
showInput.value = false; showInput.value = false;
nodeName.value = nodeName.value =
nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string); nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string);
} }
// 监听 showInput 的变化,当变为 true 时自动聚焦
watch(showInput, (value) => {
if (value) {
nextTick(() => {
inputRef.value?.focus();
});
}
});
return { return {
nodeName, nodeName,
showInput, showInput,
inputRef,
clickIcon, clickIcon,
blurEvent, changeNodeName,
}; };
} }
@ -646,11 +658,24 @@ export function useNodeName2(
) { ) {
// 显示节点名称输入框 // 显示节点名称输入框
const showInput = ref(false); const showInput = ref(false);
// 节点名称输入框失去焦点 // 输入框的引用
function blurEvent() { const inputRef = ref<HTMLInputElement | null>(null);
// 监听 showInput 的变化,当变为 true 时自动聚焦
watch(showInput, (value) => {
if (value) {
nextTick(() => {
inputRef.value?.focus();
});
}
});
// 修改节点名称
function changeNodeName() {
showInput.value = false; showInput.value = false;
node.value.name = node.value.name =
node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string); node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string);
console.warn('node.value.name===>', node.value.name);
} }
// 点击节点标题进行输入 // 点击节点标题进行输入
function clickTitle() { function clickTitle() {
@ -658,8 +683,9 @@ export function useNodeName2(
} }
return { return {
showInput, showInput,
inputRef,
clickTitle, clickTitle,
blurEvent, changeNodeName,
}; };
} }