!170 Merge remote-tracking branch 'yudao/dev' into dev

Merge pull request !170 from Jason/dev
pull/172/head
xingyu 2025-07-13 11:03:20 +00:00 committed by Gitee
commit 2150369d0d
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
8 changed files with 76 additions and 32 deletions

View File

@ -8,6 +8,7 @@ export namespace BpmProcessDefinitionApi {
id: string; id: string;
version: number; version: number;
name: string; name: string;
description: string;
deploymentTime: number; deploymentTime: number;
suspensionState: number; suspensionState: number;
modelType: number; modelType: number;

View File

@ -62,9 +62,15 @@ const [Modal, modalApi] = useVbenModal({
}, },
}); });
// TODO xingyu modalApi ? trigger-node-config.vue conditionDialog /**
// TODO @jason from xingyu useVbenModal * 打开条件配置弹窗不暴露 modalApi 给父组件
defineExpose({ modalApi }); */
function openModal(conditionObj: any) {
modalApi.setData(conditionObj).open();
}
//
defineExpose({ openModal });
</script> </script>
<template> <template>
<Modal class="w-1/2"> <Modal class="w-1/2">

View File

@ -200,8 +200,8 @@ function addFormSettingCondition(
formSetting: FormTriggerSetting, formSetting: FormTriggerSetting,
) { ) {
const conditionDialog = proxy.$refs[`condition-${index}`][0]; const conditionDialog = proxy.$refs[`condition-${index}`][0];
// 使modalApi //
conditionDialog.modalApi.setData(formSetting).open(); conditionDialog.openModal(formSetting);
} }
/** 删除条件配置 */ /** 删除条件配置 */
@ -215,8 +215,8 @@ function openFormSettingCondition(
formSetting: FormTriggerSetting, formSetting: FormTriggerSetting,
) { ) {
const conditionDialog = proxy.$refs[`condition-${index}`][0]; const conditionDialog = proxy.$refs[`condition-${index}`][0];
// 使 modalApi //
conditionDialog.modalApi.setData(formSetting).open(); conditionDialog.openModal(formSetting);
} }
/** 处理条件配置保存 */ /** 处理条件配置保存 */

View File

@ -335,7 +335,7 @@ defineExpose({ validate });
<div <div
v-for="user in selectedStartUsers" v-for="user in selectedStartUsers"
:key="user.id" :key="user.id"
class="relative flex h-9 items-center rounded-full pr-2 hover:bg-gray-200" class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2 hover:bg-gray-200 dark:border dark:border-gray-500 dark:bg-gray-700 dark:hover:bg-gray-600"
> >
<Avatar <Avatar
class="m-1" class="m-1"
@ -346,7 +346,9 @@ defineExpose({ validate });
<Avatar class="m-1" :size="28" v-else> <Avatar class="m-1" :size="28" v-else>
{{ user.nickname?.substring(0, 1) }} {{ user.nickname?.substring(0, 1) }}
</Avatar> </Avatar>
{{ user.nickname }} <span class="text-gray-700 dark:text-gray-200">
{{ user.nickname }}
</span>
<IconifyIcon <IconifyIcon
icon="lucide:x" icon="lucide:x"
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500"
@ -371,10 +373,12 @@ defineExpose({ validate });
<div <div
v-for="dept in selectedStartDepts" v-for="dept in selectedStartDepts"
:key="dept.id" :key="dept.id"
class="relative flex h-9 items-center rounded-full pr-2 shadow-sm hover:bg-gray-200" class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2 shadow-sm hover:bg-gray-200 dark:border dark:border-gray-500 dark:bg-gray-700 dark:hover:bg-gray-600"
> >
<IconifyIcon icon="lucide:building" class="size-6 px-1" /> <IconifyIcon icon="lucide:building" class="size-6 px-1" />
{{ dept.name }} <span class="text-gray-700 dark:text-gray-200">
{{ dept.name }}
</span>
<IconifyIcon <IconifyIcon
icon="lucide:x" icon="lucide:x"
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500"
@ -398,7 +402,7 @@ defineExpose({ validate });
<div <div
v-for="user in selectedManagerUsers" v-for="user in selectedManagerUsers"
:key="user.id" :key="user.id"
class="hover:bg-primary-500 relative flex h-9 items-center rounded-full pr-2" class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2 hover:bg-gray-200 dark:border dark:border-gray-500 dark:bg-gray-700 dark:hover:bg-gray-600"
> >
<Avatar <Avatar
class="m-1" class="m-1"
@ -409,7 +413,9 @@ defineExpose({ validate });
<Avatar class="m-1" :size="28" v-else> <Avatar class="m-1" :size="28" v-else>
{{ user.nickname?.substring(0, 1) }} {{ user.nickname?.substring(0, 1) }}
</Avatar> </Avatar>
{{ user.nickname }} <span class="text-gray-700 dark:text-gray-200">
{{ user.nickname }}
</span>
<IconifyIcon <IconifyIcon
icon="lucide:x" icon="lucide:x"
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500" class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500"
@ -432,6 +438,7 @@ defineExpose({ validate });
<!-- 用户选择弹窗 --> <!-- 用户选择弹窗 -->
<UserSelectModalComp <UserSelectModalComp
class="w-3/5"
v-model:value="selectedUsers" v-model:value="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户" title="选择用户"
@ -441,6 +448,7 @@ defineExpose({ validate });
/> />
<!-- 部门选择对话框 --> <!-- 部门选择对话框 -->
<DeptSelectModalComp <DeptSelectModalComp
class="w-3/5"
title="发起人部门选择" title="发起人部门选择"
:check-strictly="true" :check-strictly="true"
@confirm="handleDeptSelectConfirm" @confirm="handleDeptSelectConfirm"

View File

@ -2,7 +2,7 @@
import type { BpmCategoryApi } from '#/api/bpm/category'; import type { BpmCategoryApi } from '#/api/bpm/category';
import type { BpmProcessDefinitionApi } from '#/api/bpm/definition'; import type { BpmProcessDefinitionApi } from '#/api/bpm/definition';
import { computed, nextTick, onMounted, ref } from 'vue'; import { computed, nextTick, onMounted, ref, watch } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
@ -146,6 +146,11 @@ function handleQuery() {
// //
isSearching.value = false; isSearching.value = false;
filteredProcessDefinitionList.value = processDefinitionList.value; filteredProcessDefinitionList.value = processDefinitionList.value;
//
if (availableCategories.value.length > 0) {
activeCategory.value = availableCategories.value[0].code;
}
} }
} }
@ -216,11 +221,28 @@ const availableCategories = computed(() => {
}); });
/** 获取 tab 的位置 */ /** 获取 tab 的位置 */
const tabPosition = computed(() => { const tabPosition = computed(() => {
return window.innerWidth < 768 ? 'top' : 'left'; return window.innerWidth < 768 ? 'top' : 'left';
}); });
/** 监听可用分类变化,自动设置正确的活动分类 */
watch(
availableCategories,
(newCategories) => {
if (newCategories.length > 0) {
//
const currentCategoryExists = newCategories.some(
(category: BpmCategoryApi.Category) =>
category.code === activeCategory.value,
);
if (!currentCategoryExists) {
activeCategory.value = newCategories[0].code;
}
}
},
{ immediate: true },
);
/** 初始化 */ /** 初始化 */
onMounted(() => { onMounted(() => {
getList(); getList();
@ -241,10 +263,10 @@ onMounted(() => {
:loading="loading" :loading="loading"
> >
<template #extra> <template #extra>
<div class="flex items-end"> <div class="flex h-full items-center justify-center">
<InputSearch <InputSearch
v-model:value="searchName" v-model:value="searchName"
class="!w-50% mb-4" class="!w-50%"
placeholder="请输入流程名称检索" placeholder="请输入流程名称检索"
allow-clear allow-clear
@input="handleQuery" @input="handleQuery"
@ -260,15 +282,15 @@ onMounted(() => {
:key="category.code" :key="category.code"
:tab="category.name" :tab="category.name"
> >
<Row :gutter="[16, 16]"> <Row :gutter="[16, 16]" :wrap="true">
<Col <Col
v-for="definition in processDefinitionGroup[category.code]" v-for="definition in processDefinitionGroup[category.code]"
:key="definition.id" :key="definition.id"
:xs="24" :xs="24"
:sm="12" :sm="12"
:md="8" :md="8"
:lg="6" :lg="8"
:xl="4" :xl="6"
@click="handleSelect(definition)" @click="handleSelect(definition)"
> >
<Card <Card
@ -279,6 +301,7 @@ onMounted(() => {
}" }"
:body-style="{ :body-style="{
width: '100%', width: '100%',
padding: '16px',
}" }"
> >
<div class="flex items-center"> <div class="flex items-center">
@ -290,16 +313,14 @@ onMounted(() => {
/> />
<div v-else class="flow-icon flex-shrink-0"> <div v-else class="flow-icon flex-shrink-0">
<Tooltip :title="definition.name"> <span class="text-xs text-white">
<span class="text-xs text-white"> {{ definition.name?.slice(0, 2) }}
{{ definition.name?.slice(0, 2) }} </span>
</span>
</Tooltip>
</div> </div>
<span class="ml-3 flex-1 truncate text-base"> <span class="ml-3 flex-1 truncate text-base">
<Tooltip <Tooltip
placement="topLeft" placement="topLeft"
:title="`${definition.name}`" :title="`${definition.description}`"
> >
{{ definition.name }} {{ definition.name }}
</Tooltip> </Tooltip>

View File

@ -72,7 +72,6 @@ const timelineRef = ref<any>();
const activeTab = ref('form'); const activeTab = ref('form');
const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]); const activityNodes = ref<BpmProcessInstanceApi.ApprovalNodeInfo[]>([]);
const processInstanceStartLoading = ref(false); const processInstanceStartLoading = ref(false);
/** 提交按钮 */ /** 提交按钮 */
async function submitForm() { async function submitForm() {
if (!fApi.value || !props.selectProcessDefinition) { if (!fApi.value || !props.selectProcessDefinition) {
@ -109,7 +108,6 @@ async function submitForm() {
await router.push({ path: '/bpm/task/my' }); await router.push({ path: '/bpm/task/my' });
} catch (error) { } catch (error) {
message.error('发起流程失败');
console.error('发起流程失败:', error); console.error('发起流程失败:', error);
} finally { } finally {
processInstanceStartLoading.value = false; processInstanceStartLoading.value = false;
@ -330,7 +328,12 @@ defineExpose({ initProcessInfo });
<template #actions> <template #actions>
<template v-if="activeTab === 'form'"> <template v-if="activeTab === 'form'">
<Space wrap class="flex w-full justify-center"> <Space wrap class="flex w-full justify-center">
<Button plain type="primary" @click="submitForm"> <Button
plain
type="primary"
@click="submitForm"
:loading="processInstanceStartLoading"
>
<IconifyIcon icon="lucide:check" /> <IconifyIcon icon="lucide:check" />
发起 发起
</Button> </Button>

View File

@ -735,6 +735,7 @@ defineExpose({ loadTodoTask });
<ProcessInstanceTimeline <ProcessInstanceTimeline
:activity-nodes="nextAssigneesActivityNode" :activity-nodes="nextAssigneesActivityNode"
:show-status-icon="false" :show-status-icon="false"
:use-next-assignees="true"
@select-user-confirm="selectNextAssigneesConfirm" @select-user-confirm="selectNextAssigneesConfirm"
/> />
</div> </div>

View File

@ -20,13 +20,15 @@ import {
defineOptions({ name: 'BpmProcessInstanceTimeline' }); defineOptions({ name: 'BpmProcessInstanceTimeline' });
withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
activityNodes: BpmProcessInstanceApi.ApprovalNodeInfo[]; // activityNodes: BpmProcessInstanceApi.ApprovalNodeInfo[]; //
showStatusIcon?: boolean; // showStatusIcon?: boolean; //
useNextAssignees?: boolean; //
}>(), }>(),
{ {
showStatusIcon: true, // true showStatusIcon: true, // true
useNextAssignees: false, // false
}, },
); );
@ -196,8 +198,9 @@ function shouldShowCustomUserSelect(
isEmpty(activity.candidateUsers) && isEmpty(activity.candidateUsers) &&
(BpmCandidateStrategyEnum.START_USER_SELECT === (BpmCandidateStrategyEnum.START_USER_SELECT ===
activity.candidateStrategy || activity.candidateStrategy ||
BpmCandidateStrategyEnum.APPROVE_USER_SELECT === (BpmCandidateStrategyEnum.APPROVE_USER_SELECT ===
activity.candidateStrategy) activity.candidateStrategy &&
props.useNextAssignees))
); );
} }
@ -457,6 +460,7 @@ function handleUserSelectCancel() {
<!-- 用户选择弹窗 --> <!-- 用户选择弹窗 -->
<UserSelectModalComp <UserSelectModalComp
class="w-3/5"
v-model:value="selectedUsers" v-model:value="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户" title="选择用户"