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

Merge pull request !240 from Jason/dev
This commit is contained in:
xingyu
2025-10-27 02:28:52 +00:00
committed by Gitee
4 changed files with 159 additions and 124 deletions

View File

@@ -150,6 +150,7 @@ async function handleCategorySortSubmit() {
v-spinning="modelListSpinning" v-spinning="modelListSpinning"
> >
<template #extra> <template #extra>
<div v-if="!isCategorySorting">
<Input <Input
v-model:value="queryParams.name" v-model:value="queryParams.name"
placeholder="搜索流程" placeholder="搜索流程"
@@ -185,9 +186,8 @@ async function handleCategorySortSubmit() {
</Menu> </Menu>
</template> </template>
</Dropdown> </Dropdown>
</template> </div>
<div class="flex h-full items-center justify-between pl-5"> <div class="flex h-full items-center justify-between" v-else>
<div class="mb-4 mr-6" v-if="isCategorySorting">
<Button @click="handleCategorySortCancel" class="mr-3"> <Button @click="handleCategorySortCancel" class="mr-3">
</Button> </Button>
@@ -199,7 +199,7 @@ async function handleCategorySortSubmit() {
保存排序 保存排序
</Button> </Button>
</div> </div>
</div> </template>
<!-- 按照分类展示其所属的模型列表 --> <!-- 按照分类展示其所属的模型列表 -->
<div class="px-3" ref="categoryGroupRef"> <div class="px-3" ref="categoryGroupRef">

View File

@@ -12,7 +12,6 @@ import { IconifyIcon } from '@vben/icons';
import { useUserStore } from '@vben/stores'; import { useUserStore } from '@vben/stores';
import { cloneDeep, formatDateTime, isEqual } from '@vben/utils'; import { cloneDeep, formatDateTime, isEqual } from '@vben/utils';
// TODO @jason某个模型下的排序功能无法使用。操作流程模型分类 A 的右侧【排序】,然后,无法拖拽使用;
import { useDebounceFn } from '@vueuse/core'; import { useDebounceFn } from '@vueuse/core';
import { useSortable } from '@vueuse/integrations/useSortable'; import { useSortable } from '@vueuse/integrations/useSortable';
import { import {
@@ -65,7 +64,7 @@ const userId = useUserStore().userInfo?.id;
const isModelSorting = ref(false); const isModelSorting = ref(false);
const originalData = ref<BpmModelApi.Model[]>([]); const originalData = ref<BpmModelApi.Model[]>([]);
const modelList = ref<BpmModelApi.Model[]>([]); const modelList = ref<BpmModelApi.Model[]>([]);
// TODO @jason可以全部展开么 // TODO @jason可以全部展开么 @芋艿 上次讨论。好像是因为性能问题才只展开第一个分类
const isExpand = ref(props.isFirst); // 根据是否为第一个分类, 来设置初始展开状态 const isExpand = ref(props.isFirst); // 根据是否为第一个分类, 来设置初始展开状态
const sortableInstance = ref<any>(null); // 排序引用,以便后续启用或禁用排序 const sortableInstance = ref<any>(null); // 排序引用,以便后续启用或禁用排序
@@ -154,13 +153,17 @@ function handleModelSort() {
/** 处理模型的排序提交 */ /** 处理模型的排序提交 */
async function handleModelSortSubmit() { async function handleModelSortSubmit() {
// TODO @jasonloading 加一下,体验好点。
try {
// 确保数据已经正确同步 // 确保数据已经正确同步
if (!modelList.value || modelList.value.length === 0) { if (!modelList.value || modelList.value.length === 0) {
message.error('排序数据异常,请重试'); message.error('排序数据异常,请重试');
return; return;
} }
const hideLoading = message.loading({
content: '正在保存排序...',
duration: 0,
});
try {
// 保存排序 // 保存排序
const ids = modelList.value.map((item) => item.id); const ids = modelList.value.map((item) => item.id);
await updateModelSortBatch(ids); await updateModelSortBatch(ids);
@@ -170,6 +173,8 @@ async function handleModelSortSubmit() {
emit('success'); emit('success');
} catch (error) { } catch (error) {
console.error('排序保存失败', error); console.error('排序保存失败', error);
} finally {
hideLoading();
} }
} }
@@ -207,18 +212,29 @@ async function handleDeleteCategory() {
return; return;
} }
// TODO @jason改成 await然后增加一个 loading await confirm({
confirm({ beforeClose: async ({ isConfirm }) => {
content: `确定要删除[${props.categoryInfo.name}]吗?`, if (!isConfirm) return;
}).then(async () => {
// 发起删除 // 发起删除
const hideLoading = message.loading({
content: `正在删除分类: "${props.categoryInfo.name}"...`,
duration: 0,
});
try {
await deleteCategory(props.categoryInfo.id); await deleteCategory(props.categoryInfo.id);
} finally {
hideLoading();
}
return true;
},
content: `确定要删除[${props.categoryInfo.name}]吗?`,
icon: 'question',
});
message.success( message.success(
$t('ui.actionMessage.deleteSuccess', [props.categoryInfo.name]), $t('ui.actionMessage.deleteSuccess', [props.categoryInfo.name]),
); );
// 刷新列表 // 刷新列表
emit('success'); emit('success');
});
} }
/** 处理表单详情点击 */ /** 处理表单详情点击 */
@@ -249,21 +265,27 @@ async function modelOperation(type: string, id: number) {
/** 发布流程 */ /** 发布流程 */
async function handleDeploy(row: any) { async function handleDeploy(row: any) {
// TODO @jason改成 await然后增加一个 loading await confirm({
confirm({
beforeClose: async ({ isConfirm }) => { beforeClose: async ({ isConfirm }) => {
if (!isConfirm) return; if (!isConfirm) return;
// 发起部署 // 发起部署
const hideLoading = message.loading({
content: `正在发布流程: "${row.name}"...`,
duration: 0,
});
try {
await deployModel(row.id); await deployModel(row.id);
} finally {
hideLoading();
}
return true; return true;
}, },
content: `确认要发布[${row.name}]流程吗?`, content: `确认要发布[${row.name}]流程吗?`,
icon: 'question', icon: 'question',
}).then(async () => { });
message.success(`发布[${row.name}]流程成功`); message.success(`发布[${row.name}]流程成功`);
// 刷新列表 // 刷新列表
emit('success'); emit('success');
});
} }
/** '更多'操作按钮 */ /** '更多'操作按钮 */
@@ -300,63 +322,82 @@ function handleModelCommand(command: string, row: any) {
} }
/** 更新状态操作 */ /** 更新状态操作 */
function handleChangeState(row: any) { async function handleChangeState(row: any) {
const state = row.processDefinition.suspensionState; const state = row.processDefinition.suspensionState;
const newState = state === 1 ? 2 : 1; const newState = state === 1 ? 2 : 1;
const statusState = state === 1 ? '停用' : '启用'; const statusState = state === 1 ? '停用' : '启用';
// TODO @jason改成 await然后增加一个 loading await confirm({
confirm({
beforeClose: async ({ isConfirm }) => { beforeClose: async ({ isConfirm }) => {
if (!isConfirm) return; if (!isConfirm) return;
// 发起更新状态 // 发起更新状态
const hideLoading = message.loading({
content: `正在${statusState}流程: "${row.name}"...`,
duration: 0,
});
try {
await updateModelState(row.id, newState); await updateModelState(row.id, newState);
} finally {
hideLoading();
}
return true; return true;
}, },
content: `确认要${statusState}流程: "${row.name}" 吗?`, content: `确认要${statusState}流程: "${row.name}" 吗?`,
icon: 'question', icon: 'question',
}).then(async () => { });
message.success(`${statusState} 流程: "${row.name}" 成功`); message.success(`${statusState} 流程: "${row.name}" 成功`);
// 刷新列表 // 刷新列表
emit('success'); emit('success');
});
} }
/** 清理流程操作 */ /** 清理流程操作 */
function handleClean(row: any) { async function handleClean(row: any) {
// TODO @jason改成 await然后增加一个 loading await confirm({
confirm({
beforeClose: async ({ isConfirm }) => { beforeClose: async ({ isConfirm }) => {
if (!isConfirm) return; if (!isConfirm) return;
// 发起清理操作 // 发起清理操作
const hideLoading = message.loading({
content: `正在清理流程: "${row.name}"...`,
duration: 0,
});
try {
await cleanModel(row.id); await cleanModel(row.id);
} finally {
hideLoading();
}
return true; return true;
}, },
content: `确认要清理流程: "${row.name}" 吗?`, content: `确认要清理流程: "${row.name}" 吗?`,
icon: 'question', icon: 'question',
}).then(async () => { });
message.success(`清理流程: "${row.name}" 成功`); message.success(`清理流程: "${row.name}" 成功`);
// 刷新列表 // 刷新列表
emit('success'); emit('success');
});
} }
/** 删除流程操作 */ /** 删除流程操作 */
function handleDelete(row: any) { async function handleDelete(row: any) {
// TODO @jason改成 await然后增加一个 loading await confirm({
confirm({
beforeClose: async ({ isConfirm }) => { beforeClose: async ({ isConfirm }) => {
if (!isConfirm) return; if (!isConfirm) return;
// 发起删除操作 // 发起删除操作
const hideLoading = message.loading({
content: $t('ui.actionMessage.deleting', [row.name]),
duration: 0,
});
try {
await deleteModel(row.id); await deleteModel(row.id);
} finally {
hideLoading();
}
return true; return true;
}, },
content: `确认要删除流程: "${row.name}" 吗?`, content: `确认要删除流程: "${row.name}" 吗?`,
icon: 'question', icon: 'question',
}).then(async () => { });
message.success(`删除流程: "${row.name}" 成功`); message.success(`删除流程: "${row.name}" 成功`);
// 刷新列表 // 刷新列表
emit('success'); emit('success');
});
} }
/** 跳转到指定流程定义列表 */ /** 跳转到指定流程定义列表 */
@@ -422,12 +463,14 @@ function handleRenameSuccess() {
> >
<div class="flex h-12 items-center"> <div class="flex h-12 items-center">
<!-- 头部分类名 --> <!-- 头部分类名 -->
<!-- TODO @jason1无法拖动排序2拖动后直接请求排序不用有个保存排序模型分类和排序分类里的模型交互有点不同哈 --> <!-- TODO @jason2拖动后直接请求排序不用有个保存排序模型分类和排序分类里的模型交互有点不同哈@芋艿 好像 yudao-ui-admin-vue3 交互也是这样的需要改吗? -->
<div class="flex items-center"> <div class="flex items-center">
<Tooltip v-if="isCategorySorting" title="拖动排序"> <Tooltip v-if="isCategorySorting" title="拖动排序">
<span <!-- drag-handle 标识可以拖动不能删掉 -->
class="icon-[ic--round-drag-indicator] ml-2.5 cursor-move text-2xl text-gray-500" <IconifyIcon
></span> icon="ic:round-drag-indicator"
class="drag-handle ml-2.5 cursor-move text-2xl text-gray-500"
/>
</Tooltip> </Tooltip>
<div class="ml-4 mr-2 text-lg font-medium"> <div class="ml-4 mr-2 text-lg font-medium">
{{ categoryInfo.name }} {{ categoryInfo.name }}
@@ -517,15 +560,16 @@ function handleRenameSuccess() {
:class="`category-${categoryInfo.id}`" :class="`category-${categoryInfo.id}`"
> >
<template #name="{ row }"> <template #name="{ row }">
<div class="flex items-center"> <div class="flex items-center overflow-hidden">
<Tooltip <Tooltip
v-if="isModelSorting" v-if="isModelSorting"
title="拖动排序" title="拖动排序"
placement="left" placement="left"
> >
<!-- drag-handle 标识用于推动排序 useSortable 用到 -->
<IconifyIcon <IconifyIcon
icon="ic:round-drag-indicator" icon="ic:round-drag-indicator"
class="mr-2.5 cursor-move text-2xl text-gray-500" class="drag-handle mr-2.5 flex-shrink-0 cursor-move text-2xl text-gray-500"
/> />
</Tooltip> </Tooltip>
<div <div
@@ -542,10 +586,12 @@ function handleRenameSuccess() {
class="mr-2.5 h-9 w-9 flex-shrink-0 rounded" class="mr-2.5 h-9 w-9 flex-shrink-0 rounded"
alt="图标" alt="图标"
/> />
<EllipsisText :max-width="160" :tooltip-when-ellipsis="true"> <div class="min-w-0 overflow-hidden">
<EllipsisText :tooltip-when-ellipsis="true">
{{ row.name }} {{ row.name }}
</EllipsisText> </EllipsisText>
</div> </div>
</div>
</template> </template>
<template #startUserIds="{ row }"> <template #startUserIds="{ row }">
<span v-if="!row.startUsers?.length && !row.startDepts?.length"> <span v-if="!row.startUsers?.length && !row.startDepts?.length">

View File

@@ -6,6 +6,7 @@ import { nextTick, onMounted, ref, shallowRef, watch } from 'vue';
import { Page } from '@vben/common-ui'; import { Page } from '@vben/common-ui';
import { import {
BpmFieldPermissionType,
BpmModelFormType, BpmModelFormType,
BpmModelType, BpmModelType,
BpmTaskStatusEnum, BpmTaskStatusEnum,
@@ -44,13 +45,6 @@ const props = defineProps<{
taskId?: string; // 任务编号 taskId?: string; // 任务编号
}>(); }>();
// TODO @jason是不是使用全局的 FieldPermissionTypeexport enum FieldPermissionType {
enum FieldPermissionType {
NONE = '3', // 隐藏
READ = '1', // 只读
WRITE = '2', // 编辑
}
const processInstanceLoading = ref(false); // 流程实例的加载中 const processInstanceLoading = ref(false); // 流程实例的加载中
const processInstance = ref<BpmProcessInstanceApi.ProcessInstance>(); // 流程实例 const processInstance = ref<BpmProcessInstanceApi.ProcessInstance>(); // 流程实例
const processDefinition = ref<any>({}); // 流程定义 const processDefinition = ref<any>({}); // 流程定义
@@ -131,8 +125,7 @@ async function getApprovalDetail() {
processInstance.value.formVariables, processInstance.value.formVariables,
); );
} }
// TODO @jason这里 await 来搞? await nextTick();
nextTick().then(() => {
fApi.value?.btn.show(false); fApi.value?.btn.show(false);
fApi.value?.resetBtn.show(false); fApi.value?.resetBtn.show(false);
fApi.value?.disabled(true); fApi.value?.disabled(true);
@@ -142,7 +135,6 @@ async function getApprovalDetail() {
setFieldPermission(item, formFieldsPermission[item]); setFieldPermission(item, formFieldsPermission[item]);
}); });
} }
});
} else { } else {
// 注意data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue // 注意data.processDefinition.formCustomViewPath 是组件的全路径,例如说:/crm/contract/detail/index.vue
BusinessFormComponent.value = registerComponent( BusinessFormComponent.value = registerComponent(
@@ -178,15 +170,15 @@ async function getProcessModelView() {
/** 设置表单权限 */ /** 设置表单权限 */
function setFieldPermission(field: string, permission: string) { function setFieldPermission(field: string, permission: string) {
if (permission === FieldPermissionType.READ) { if (permission === BpmFieldPermissionType.READ) {
fApi.value?.disabled(true, field); fApi.value?.disabled(true, field);
} }
if (permission === FieldPermissionType.WRITE) { if (permission === BpmFieldPermissionType.WRITE) {
fApi.value?.disabled(false, field); fApi.value?.disabled(false, field);
// 加入可以编辑的字段 // 加入可以编辑的字段
writableFields.push(field); writableFields.push(field);
} }
if (permission === FieldPermissionType.NONE) { if (permission === BpmFieldPermissionType.NONE) {
fApi.value?.hidden(true, field); fApi.value?.hidden(true, field);
} }
} }
@@ -203,13 +195,11 @@ function setFieldPermission(field: string, permission: string) {
/** 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 */ /** 监听 Tab 切换,当切换到 "record" 标签时刷新任务列表 */
watch( watch(
() => activeTab.value, () => activeTab.value,
(newVal) => { async (newVal) => {
if (newVal === 'record') { if (newVal === 'record') {
// 如果切换到流转记录标签,刷新任务列表 // 如果切换到流转记录标签,刷新任务列表
// TODO @jasonawait nextTick 要不? await nextTick();
nextTick(() => {
taskListRef.value?.refresh(); taskListRef.value?.refresh();
});
} }
}, },
); );

View File

@@ -558,9 +558,8 @@ async function handleCancel() {
/** 处理再次提交 */ /** 处理再次提交 */
async function handleReCreate() { async function handleReCreate() {
// 跳转发起流程界面 // 跳转发起流程界面
// TODO @jason这个要优化成 push 到 name 么?这样后续 path 可以按需调整;
await router.push({ await router.push({
path: '/bpm/task/create', name: 'BpmProcessInstanceCreate',
query: { processInstanceId: props.processInstance?.id }, query: { processInstanceId: props.processInstance?.id },
}); });
} }