Files
yudao-ui-admin-vben/apps/web-antd/src/views/bpm/model/index.vue
2025-06-27 23:18:42 +08:00

225 lines
6.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import type { ModelCategoryInfo } from '#/api/bpm/model';
import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
import { cloneDeep } from '@vben/utils';
import { useSortable } from '@vueuse/integrations/useSortable';
import { Button, Card, Dropdown, Input, Menu, message } from 'ant-design-vue';
import {
getCategorySimpleList,
updateCategorySortBatch,
} from '#/api/bpm/category';
import { getModelList } from '#/api/bpm/model';
import { router } from '#/router';
// 流程分类对话框
import CategoryForm from '../category/modules/form.vue';
import CategoryDraggableModel from './modules/category-draggable-model.vue';
// 新建流程分类对话框
const [CategoryFormModal, categoryFormModalApi] = useVbenModal({
connectedComponent: CategoryForm,
destroyOnClose: true,
});
// 模型列表加载状态
const modelListSpinning = ref(false);
// 保存排序状态
const saveSortLoading = ref(false);
// 按照 category 分组的数据
const categoryGroup = ref<ModelCategoryInfo[]>([]);
// 未排序前的原始数据
const originalData = ref<ModelCategoryInfo[]>([]);
// 可以排序元素的容器
const sortable = useTemplateRef<HTMLElement>('categoryGroupRef');
// 排序引用,以便后续启用或禁用排序
const sortableInstance = ref<any>(null);
// 分类排序状态
const isCategorySorting = ref(false);
// 查询参数
const queryParams = reactive({
name: '',
});
// 监听分类排序模式切换
watch(
() => isCategorySorting.value,
(newValue) => {
if (sortableInstance.value) {
if (newValue) {
// 启用排序功能
sortableInstance.value.option('disabled', false);
} else {
// 禁用排序功能
sortableInstance.value.option('disabled', true);
}
}
},
);
/** 加载数据 */
async function getList() {
modelListSpinning.value = true;
try {
const modelList = await getModelList(queryParams.name);
const categoryList = await getCategorySimpleList();
// 按照 category 聚合
categoryGroup.value = categoryList.map((category: any) => ({
...category,
modelList: modelList.filter(
(model: any) => model.categoryName === category.name,
),
}));
// 重置排序实例
sortableInstance.value = null;
} finally {
modelListSpinning.value = false;
}
}
/** 初始化 */
onActivated(() => {
getList();
});
/** 新增模型 */
function createModel() {
router.push({
name: 'BpmModelCreate',
});
}
/** 处理下拉菜单命令 */
function handleCommand(command: string) {
if (command === 'handleCategoryAdd') {
// 打开新建流程分类弹窗
categoryFormModalApi.open();
} else if (command === 'handleCategorySort') {
originalData.value = cloneDeep(categoryGroup.value);
isCategorySorting.value = true;
// 如果排序实例不存在,则初始化
if (sortableInstance.value) {
// 已存在实例,则启用排序功能
sortableInstance.value.option('disabled', false);
} else {
sortableInstance.value = useSortable(sortable, categoryGroup, {
disabled: false, // 启用排序
});
}
}
}
/** 取消分类排序 */
function handleCategorySortCancel() {
// 恢复初始数据
categoryGroup.value = cloneDeep(originalData.value);
isCategorySorting.value = false;
// 直接禁用排序功能
if (sortableInstance.value) {
sortableInstance.value.option('disabled', true);
}
}
/** 提交分类排序 */
async function handleCategorySortSubmit() {
saveSortLoading.value = true;
try {
// 保存排序逻辑
const ids = categoryGroup.value.map((item: any) => item.id);
await updateCategorySortBatch(ids);
} finally {
saveSortLoading.value = false;
}
message.success('分类排序成功');
isCategorySorting.value = false;
// 刷新列表
await getList();
// 禁用排序功能
if (sortableInstance.value) {
sortableInstance.value.option('disabled', true);
}
}
</script>
<template>
<Page auto-content-height>
<!-- 流程分类表单弹窗 -->
<CategoryFormModal @success="getList" />
<Card
:body-style="{ padding: '10px' }"
class="mb-4"
title="流程模型"
v-spinning="modelListSpinning"
>
<template #extra>
<Input
v-model:value="queryParams.name"
placeholder="搜索流程"
allow-clear
@press-enter="getList"
class="!w-60"
/>
<Button class="ml-2" type="primary" @click="createModel">
<IconifyIcon icon="lucide:plus" /> 新建模型
</Button>
<Dropdown class="ml-2" placement="bottomRight" arrow>
<Button>
<template #icon>
<div class="flex items-center justify-center">
<IconifyIcon icon="lucide:settings" />
</div>
</template>
</Button>
<template #overlay>
<Menu @click="(e) => handleCommand(e.key as string)">
<Menu.Item key="handleCategoryAdd">
<div class="flex items-center gap-1">
<IconifyIcon icon="lucide:plus" />
新建分类
</div>
</Menu.Item>
<Menu.Item key="handleCategorySort">
<div class="flex items-center gap-1">
<IconifyIcon icon="lucide:align-start-vertical" />
分类排序
</div>
</Menu.Item>
</Menu>
</template>
</Dropdown>
</template>
<div class="flex h-full items-center justify-between pl-5">
<div class="mb-4 mr-6" v-if="isCategorySorting">
<Button @click="handleCategorySortCancel" class="mr-3">
</Button>
<Button
type="primary"
:loading="saveSortLoading"
@click="handleCategorySortSubmit"
>
保存排序
</Button>
</div>
</div>
<!-- 按照分类展示其所属的模型列表 -->
<div class="px-3" ref="categoryGroupRef">
<CategoryDraggableModel
v-for="(element, index) in categoryGroup"
:class="isCategorySorting ? 'cursor-move' : ''"
:key="element.id"
:category-info="element"
:is-category-sorting="isCategorySorting"
:is-first="index === 0"
@success="getList"
/>
</div>
</Card>
</Page>
</template>