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

Merge pull request !289 from Jason/dev
This commit is contained in:
芋道源码
2025-12-01 16:50:15 +00:00
committed by Gitee
9 changed files with 112 additions and 175 deletions

View File

@@ -9,24 +9,6 @@ const routes: RouteRecordRaw[] = [
hideInMenu: true,
},
children: [
{
path: 'task',
name: 'BpmTask',
meta: {
title: '审批中心',
icon: 'ant-design:history-outlined',
},
children: [
{
path: 'my',
name: 'BpmTaskMy',
component: () => import('#/views/bpm/processInstance/index.vue'),
meta: {
title: '我的流程',
},
},
],
},
{
path: 'process-instance/detail',
component: () => import('#/views/bpm/processInstance/detail/index.vue'),

View File

@@ -74,7 +74,6 @@ const assignEmptyUserIdsEl = ref<any>();
const assignEmptyUserIds = ref<any>();
// 操作按钮
// TODO @puhui999这块迁移有点问题按钮不能操作另外label 也没展示。
const buttonsSettingEl = ref<any>();
const { btnDisplayNameEdit, changeBtnDisplayName } = useButtonsSetting();
const btnDisplayNameBlurEvent = (index: number) => {
@@ -179,7 +178,7 @@ const resetCustomConfigList = () => {
});
// 操作按钮
buttonsSettingEl.value = elExtensionElements.value.values?.find(
buttonsSettingEl.value = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:ButtonsSetting`,
);
if (buttonsSettingEl.value.length === 0) {
@@ -196,7 +195,7 @@ const resetCustomConfigList = () => {
// 字段权限
if (formType.value === BpmModelFormType.NORMAL) {
const fieldsPermissionList = elExtensionElements.value.values?.find(
const fieldsPermissionList = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:FieldsPermission`,
);
fieldsPermissionEl.value = [];
@@ -373,7 +372,6 @@ function useButtonsSetting() {
}
/** 批量更新权限 */
// TODO @lesan这个页面有一些 idea 红色报错,咱要不要 fix 下!
const updatePermission = (type: string) => {
fieldsPermissionEl.value.forEach((field: any) => {
if (type === 'READ') {

View File

@@ -259,9 +259,11 @@ async function validateAllSteps() {
return true;
}
const saveLoading = ref<boolean>(false);
/** 保存操作 */
async function handleSave() {
try {
saveLoading.value = true;
// 保存前校验所有步骤的数据
const result = await validateAllSteps();
if (!result) {
@@ -309,9 +311,12 @@ async function handleSave() {
}
} catch (error: any) {
console.error('保存失败:', error);
} finally {
saveLoading.value = false;
}
}
// 发布加载中状态
const deployLoading = ref<boolean>(false);
/** 发布操作 */
async function handleDeploy() {
try {
@@ -319,6 +324,7 @@ async function handleDeploy() {
if (!formData.value.id) {
await confirm('是否确认发布该流程?');
}
deployLoading.value = true;
// 1.2 校验所有步骤
await validateAllSteps();
@@ -342,6 +348,8 @@ async function handleDeploy() {
} catch (error: any) {
console.error('发布失败:', error);
message.warning(error.message || '发布失败');
} finally {
deployLoading.value = false;
}
}
@@ -448,11 +456,12 @@ onBeforeUnmount(() => {
<Button
v-if="actionType === 'update'"
type="primary"
:loading="deployLoading"
@click="handleDeploy"
>
</Button>
<Button type="primary" @click="handleSave">
<Button type="primary" @click="handleSave" :loading="saveLoading">
<span v-if="actionType === 'definition'"> </span>
<span v-else> </span>
</Button>

View File

@@ -228,9 +228,10 @@ onMounted(() => {
>
<Card
hoverable
class="definition-item-card w-full cursor-pointer"
class="w-full cursor-pointer"
:class="{
'search-match': searchName.trim().length > 0,
'animate-bounce-once !bg-[rgb(63_115_247_/_10%)]':
searchName.trim().length > 0,
}"
:body-style="{
width: '100%',
@@ -241,10 +242,13 @@ onMounted(() => {
<img
v-if="definition.icon"
:src="definition.icon"
class="flow-icon-img object-contain"
class="size-12 rounded object-contain"
alt="流程图标"
/>
<div v-else class="flow-icon flex-shrink-0">
<div
v-else
class="flex size-12 flex-shrink-0 items-center justify-center rounded bg-primary"
>
<span class="text-xs text-white">
{{ definition.name?.slice(0, 2) }}
</span>
@@ -283,7 +287,6 @@ onMounted(() => {
</template>
<style lang="scss" scoped>
// @jason看看能不能通过 tailwindcss 简化下
@keyframes bounce {
0%,
50% {
@@ -295,30 +298,7 @@ onMounted(() => {
}
}
.process-definition-container {
.definition-item-card {
.flow-icon-img {
width: 48px;
height: 48px;
border-radius: 0.25rem;
}
.flow-icon {
@apply bg-primary;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 0.25rem;
}
&.search-match {
background-color: rgb(63 115 247 / 10%);
border: 1px solid var(--primary);
animation: bounce 0.5s ease;
}
}
.animate-bounce-once {
animation: bounce 0.5s ease;
}
</style>

View File

@@ -104,7 +104,7 @@ async function submitForm() {
// 关闭并提示
message.success('发起流程成功');
await closeCurrentTab();
await router.push({ name: 'BpmTaskMy' });
await router.push({ name: 'BpmProcessInstanceMy' });
} finally {
processInstanceStartLoading.value = false;
}

View File

@@ -212,20 +212,27 @@ watch(
}
},
);
const loading = ref(false);
/** 初始化 */
onMounted(async () => {
await getDetail();
// 获得用户列表
userOptions.value = await getSimpleUserList();
try {
loading.value = true;
await getDetail();
// 获得用户列表
userOptions.value = await getSimpleUserList();
} finally {
loading.value = false;
}
});
</script>
<template>
<Page auto-content-height>
<Page auto-content-height v-loading="loading">
<Card
class="flex h-full flex-col"
:body-style="{
overflowY: 'auto',
flex: 1,
overflowY: 'hidden',
paddingTop: '12px',
}"
>
@@ -286,24 +293,16 @@ onMounted(async () => {
</div>
<!-- 流程操作 -->
<div class="process-tabs-container flex flex-1 flex-col">
<Tabs v-model:active-key="activeTab" class="mt-0 h-full">
<TabPane tab="审批详情" key="form" class="tab-pane-content">
<Row :gutter="[48, 24]" class="h-full">
<Col
:xs="24"
:sm="24"
:md="18"
:lg="18"
:xl="16"
class="h-full"
>
<div class="flex h-full flex-1 flex-col">
<Tabs v-model:active-key="activeTab">
<TabPane tab="审批详情" key="form" class="pb-20 pr-3">
<Row :gutter="[48, 24]">
<Col :xs="24" :sm="24" :md="18" :lg="18" :xl="16">
<!-- 流程表单 -->
<div
v-if="
processDefinition?.formType === BpmModelFormType.NORMAL
"
class="h-full"
>
<form-create
v-model="detailForm.value"
@@ -316,13 +315,12 @@ onMounted(async () => {
v-else-if="
processDefinition?.formType === BpmModelFormType.CUSTOM
"
class="h-full"
>
<BusinessFormComponent :id="processInstance?.businessKey" />
</div>
</Col>
<Col :xs="24" :sm="24" :md="6" :lg="6" :xl="8" class="h-full">
<div class="mt-4 h-full">
<Col :xs="24" :sm="24" :md="6" :lg="6" :xl="8">
<div class="mt-4">
<ProcessInstanceTimeline :activity-nodes="activityNodes" />
</div>
</Col>
@@ -331,44 +329,35 @@ onMounted(async () => {
<TabPane
tab="流程图"
key="diagram"
class="tab-pane-content"
class="pb-20 pr-3"
:force-render="true"
>
<div class="h-full">
<ProcessInstanceSimpleViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.SIMPLE
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
<ProcessInstanceBpmnViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.BPMN
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
</div>
<ProcessInstanceSimpleViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.SIMPLE
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
<ProcessInstanceBpmnViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.BPMN
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
</TabPane>
<TabPane tab="流转记录" key="record" class="tab-pane-content">
<div class="h-full">
<BpmProcessInstanceTaskList
ref="taskListRef"
:loading="processInstanceLoading"
:id="id"
/>
</div>
<TabPane tab="流转记录" key="record" class="pb-20 pr-3">
<BpmProcessInstanceTaskList
ref="taskListRef"
:loading="processInstanceLoading"
:id="id"
/>
</TabPane>
<!-- TODO 待开发 -->
<TabPane
tab="流转评论"
key="comment"
v-if="false"
class="tab-pane-content"
>
<TabPane tab="流转评论" key="comment" v-if="false" class="pr-3">
<div class="h-full">待开发</div>
</TabPane>
</Tabs>
@@ -396,35 +385,18 @@ onMounted(async () => {
</template>
<style lang="scss" scoped>
// @jason看看能不能通过 tailwindcss 简化下
.ant-tabs-content {
height: 100%;
}
.process-tabs-container {
display: flex;
flex-direction: column;
height: 100%;
}
:deep(.ant-tabs) {
display: flex;
flex-direction: column;
height: 100%;
}
:deep(.ant-tabs-content) {
flex: 1;
overflow-y: auto;
.ant-tabs-content {
height: 100%;
}
}
:deep(.ant-tabs-tabpane) {
height: 100%;
}
.tab-pane-content {
height: calc(100vh - 420px);
padding-right: 12px;
overflow: hidden auto;
overflow-y: auto;
}
</style>

View File

@@ -5,7 +5,7 @@ import { useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { base64ToFile } from '@vben/utils';
import { Button, Space, Tooltip } from 'ant-design-vue';
import { Button, Tooltip } from 'ant-design-vue';
import Vue3Signature from 'vue3-signature';
import { uploadFile } from '#/api/infra/file';
@@ -36,30 +36,29 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal title="流程签名" class="w-3/5">
<div class="mb-2 flex justify-end">
<Space>
<div class="flex h-[50vh] flex-col">
<div class="mb-2 flex justify-end gap-2">
<Tooltip title="撤销上一步操作">
<Button @click="signature?.undo()">
<Button @click="signature?.undo()" size="small">
<template #icon>
<IconifyIcon icon="lucide:undo" class="mb-1 size-4" />
<IconifyIcon icon="lucide:undo" class="mb-1 size-3" />
</template>
撤销
</Button>
</Tooltip>
<Tooltip title="清空画布">
<Button @click="signature?.clear()">
<Button @click="signature?.clear()" size="small">
<template #icon>
<IconifyIcon icon="lucide:trash" class="mb-1 size-4" />
<IconifyIcon icon="lucide:trash" class="mb-1 size-3" />
</template>
<span>清除</span>
</Button>
</Tooltip>
</Space>
</div>
<Vue3Signature
class="h-full flex-1 border border-solid border-gray-300"
ref="signature"
/>
</div>
<Vue3Signature
class="mx-auto !h-80 border border-solid border-gray-300"
ref="signature"
/>
</Modal>
</template>

View File

@@ -44,7 +44,7 @@ function useGridColumns(): VxeTableGridOptions['columns'] {
field: 'approver',
title: '审批人',
slots: {
default: ({ row }: { row: BpmTaskApi.TaskManager }) => {
default: ({ row }: { row: BpmTaskApi.Task }) => {
return row.assigneeUser?.nickname || row.ownerUser?.nickname;
},
},
@@ -106,7 +106,7 @@ function handleRefresh() {
}
/** 显示表单详情 */
async function handleShowFormDetail(row: BpmTaskApi.TaskManager) {
async function handleShowFormDetail(row: BpmTaskApi.Task) {
// 设置表单配置和表单字段
taskForm.value = {
rule: [],
@@ -141,7 +141,6 @@ const [Grid, gridApi] = useVbenVxeGrid({
keepSource: true,
showFooter: true,
border: true,
height: 'auto',
proxyConfig: {
ajax: {
query: async () => {
@@ -159,7 +158,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
toolbarConfig: {
enabled: false,
},
} as VxeTableGridOptions<BpmTaskApi.TaskManager>,
} as VxeTableGridOptions<BpmTaskApi.Task>,
});
defineExpose({
@@ -168,7 +167,7 @@ defineExpose({
</script>
<template>
<div class="flex h-full flex-col">
<div>
<Grid>
<template #slot-reason="{ row }">
<div class="flex flex-wrap items-center justify-center">
@@ -188,13 +187,13 @@ defineExpose({
</div>
</template>
</Grid>
<Modal class="w-[800px]">
<form-create
ref="formRef"
v-model="taskForm.value"
:option="taskForm.option"
:rule="taskForm.rule"
/>
</Modal>
</div>
<Modal class="w-3/5">
<form-create
ref="formRef"
v-model="taskForm.value"
:option="taskForm.option"
:rule="taskForm.rule"
/>
</Modal>
</template>

View File

@@ -344,24 +344,22 @@ onMounted(async () => {
</ElRow>
</ElTabPane>
<ElTabPane label="流程图" name="diagram" class="pb-20 pr-3">
<div>
<ProcessInstanceSimpleViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.SIMPLE
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
<ProcessInstanceBpmnViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.BPMN
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
</div>
<ProcessInstanceSimpleViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.SIMPLE
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
<ProcessInstanceBpmnViewer
v-show="
processDefinition.modelType &&
processDefinition.modelType === BpmModelType.BPMN
"
:loading="processInstanceLoading"
:model-view="processModelView"
/>
</ElTabPane>
<ElTabPane label="流转记录" name="record" class="pb-20 pr-3">
<BpmProcessInstanceTaskList