!93 Merge remote-tracking branch 'yudao/dev' into dev
Merge pull request !93 from Jason/devpull/94/MERGE
						commit
						b7673f800a
					
				|  | @ -1,7 +1,5 @@ | |||
| import type { PageParam, PageResult } from '@vben/request'; | ||||
| 
 | ||||
| import type { BpmModelApi } from '#/api/bpm/model'; | ||||
| 
 | ||||
| import { requestClient } from '#/api/request'; | ||||
| 
 | ||||
| export namespace BpmCategoryApi { | ||||
|  | @ -11,16 +9,9 @@ export namespace BpmCategoryApi { | |||
|     name: string; | ||||
|     code: string; | ||||
|     status: number; | ||||
|     description?: string; | ||||
|     sort: number; // 分类排序
 | ||||
|   } | ||||
| 
 | ||||
|   /** 模型分类信息 */ | ||||
|   // TODO @jason:这个应该非 api 的,可以考虑抽到页面里哈。
 | ||||
|   export interface ModelCategoryInfo { | ||||
|     id: number; | ||||
|     name: string; | ||||
|     modelList: BpmModelApi.ModelVO[]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 查询流程分类分页 */ | ||||
|  |  | |||
|  | @ -40,13 +40,13 @@ export namespace BpmModelApi { | |||
|     bpmnXml: string; | ||||
|     startUsers?: UserInfo[]; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|   /** 模型分类信息 */ | ||||
|   export interface ModelCategoryInfo { | ||||
|     id: number; | ||||
|     name: string; | ||||
|     modelList: ModelVO[]; | ||||
|   } | ||||
| /** 模型分类信息 */ | ||||
| export interface ModelCategoryInfo { | ||||
|   id: number; | ||||
|   name: string; | ||||
|   modelList: BpmModelApi.ModelVO[]; | ||||
| } | ||||
| 
 | ||||
| /** 获取流程模型列表 */ | ||||
|  |  | |||
|  | @ -64,6 +64,7 @@ export function useFormSchema(): VbenFormSchema[] { | |||
|         min: 0, | ||||
|         controlsPosition: 'right', | ||||
|         placeholder: '请输入分类排序', | ||||
|         class: 'w-full', | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
|  |  | |||
|  | @ -0,0 +1,102 @@ | |||
| <script lang="ts" setup> | ||||
| import type { BpmCategoryApi } from '#/api/bpm/category'; | ||||
| 
 | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { useVbenForm } from '#/adapter/form'; | ||||
| import { getCategory, updateCategory } from '#/api/bpm/category'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| const emit = defineEmits(['success']); | ||||
| const formData = ref<BpmCategoryApi.CategoryVO>(); | ||||
| 
 | ||||
| // 定义表单结构 | ||||
| const formSchema = [ | ||||
|   { | ||||
|     fieldName: 'name', | ||||
|     label: '分类名', | ||||
|     component: 'Input', | ||||
|     componentProps: { | ||||
|       placeholder: '请输入分类名', | ||||
|     }, | ||||
|     rules: 'required', | ||||
|   }, | ||||
| ]; | ||||
| 
 | ||||
| // 创建表单 | ||||
| const [Form, formApi] = useVbenForm({ | ||||
|   layout: 'horizontal', | ||||
|   schema: formSchema, | ||||
|   showDefaultActions: false, | ||||
| }); | ||||
| 
 | ||||
| // 创建模态窗 | ||||
| const [Modal, modalApi] = useVbenModal({ | ||||
|   // 保存按钮回调 | ||||
|   async onConfirm() { | ||||
|     const { valid } = await formApi.validate(); | ||||
|     if (!valid) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
| 
 | ||||
|     // 提交表单,只更新流程分类名 | ||||
|     const formValues = await formApi.getValues(); | ||||
|     const data = { | ||||
|       id: formData.value?.id, | ||||
|       name: formValues.name, // 只更新流程分类名 | ||||
|       code: formData.value?.code, | ||||
|       status: formData.value?.status, | ||||
|       description: formData.value?.description, | ||||
|       sort: formData.value?.sort, | ||||
|     } as BpmCategoryApi.CategoryVO; | ||||
| 
 | ||||
|     try { | ||||
|       await updateCategory(data); | ||||
|       // 关闭并提示 | ||||
|       await modalApi.close(); | ||||
|       emit('success'); | ||||
|       message.success($t('ui.actionMessage.operationSuccess')); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
| 
 | ||||
|   // 打开/关闭弹窗回调 | ||||
|   async onOpenChange(isOpen: boolean) { | ||||
|     if (!isOpen) { | ||||
|       formData.value = undefined; | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     // 加载数据 | ||||
|     const data = modalApi.getData<BpmCategoryApi.CategoryVO>(); | ||||
| 
 | ||||
|     if (!data || !data.id) { | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     modalApi.lock(); | ||||
|     try { | ||||
|       // 获取流程分类数据 | ||||
|       formData.value = await getCategory(data.id as number); | ||||
|       // 仅设置 name 字段 | ||||
|       await formApi.setValues({ | ||||
|         name: formData.value.name, | ||||
|       }); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Modal title="重命名流程分类"> | ||||
|     <Form class="mx-4" /> | ||||
|   </Modal> | ||||
| </template> | ||||
|  | @ -1,9 +1,9 @@ | |||
| <script lang="ts" setup> | ||||
| import type { BpmModelApi } from '#/api/bpm/model'; | ||||
| import type { ModelCategoryInfo } from '#/api/bpm/model'; | ||||
| 
 | ||||
| import { onActivated, reactive, ref, useTemplateRef, watch } from 'vue'; | ||||
| 
 | ||||
| import { Page } from '@vben/common-ui'; | ||||
| import { Page, useVbenModal } from '@vben/common-ui'; | ||||
| import { Plus, Search, Settings } from '@vben/icons'; | ||||
| import { cloneDeep } from '@vben/utils'; | ||||
| 
 | ||||
|  | @ -26,15 +26,24 @@ import { | |||
| } from '#/api/bpm/category'; | ||||
| import { getModelList } from '#/api/bpm/model'; | ||||
| 
 | ||||
| // 流程分类对话框 | ||||
| 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 = refAutoReset(false, 3000); | ||||
| // 保存排序状态 | ||||
| const saveSortLoading = ref(false); | ||||
| // 按照 category 分组的数据 | ||||
| const categoryGroup = ref<BpmModelApi.ModelCategoryInfo[]>([]); | ||||
| const categoryGroup = ref<ModelCategoryInfo[]>([]); | ||||
| // 未排序前的原始数据 | ||||
| const originalData = ref<BpmModelApi.ModelCategoryInfo[]>([]); | ||||
| const originalData = ref<ModelCategoryInfo[]>([]); | ||||
| // 可以排序元素的容器 | ||||
| const sortable = useTemplateRef<HTMLElement>('categoryGroupRef'); | ||||
| // 排序引用,以便后续启用或禁用排序 | ||||
|  | @ -100,7 +109,8 @@ const createModel = () => { | |||
| /** 处理下拉菜单命令 */ | ||||
| const handleCommand = (command: string) => { | ||||
|   if (command === 'handleCategoryAdd') { | ||||
|     //  TODO 新建分类逻辑 | ||||
|     // 打开新建流程分类弹窗 | ||||
|     categoryFormModalApi.open(); | ||||
|   } else if (command === 'handleCategorySort') { | ||||
|     originalData.value = cloneDeep(categoryGroup.value); | ||||
|     isCategorySorting.value = true; | ||||
|  | @ -152,7 +162,7 @@ const handleCategorySortSubmit = async () => { | |||
|   <Page auto-content-height> | ||||
|     <Card | ||||
|       :body-style="{ padding: '10px' }" | ||||
|       class="mb-4 h-[89vh]" | ||||
|       class="mb-4" | ||||
|       v-spinning="modelListSpinning" | ||||
|     > | ||||
|       <div class="flex h-full items-center justify-between pl-5"> | ||||
|  | @ -184,7 +194,7 @@ const handleCategorySortSubmit = async () => { | |||
|             </Button> | ||||
|           </Form.Item> | ||||
|           <Form.Item> | ||||
|             <Dropdown placement="bottomRight"> | ||||
|             <Dropdown placement="bottomRight" arrow> | ||||
|               <Button> | ||||
|                 <template #icon> | ||||
|                   <Settings class="size-4" /> | ||||
|  | @ -239,4 +249,7 @@ const handleCategorySortSubmit = async () => { | |||
|       </div> | ||||
|     </Card> | ||||
|   </Page> | ||||
| 
 | ||||
|   <!-- 流程分类表单弹窗 --> | ||||
|   <CategoryFormModal @success="getList" /> | ||||
| </template> | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| <script lang="ts" setup> | ||||
| import type { BpmCategoryApi } from '#/api/bpm/category'; | ||||
| import type { BpmModelApi } from '#/api/bpm/model'; | ||||
| import type { BpmModelApi, ModelCategoryInfo } from '#/api/bpm/model'; | ||||
| 
 | ||||
| import { computed, ref, watchEffect } from 'vue'; | ||||
| 
 | ||||
| import { confirm, useVbenModal } from '@vben/common-ui'; | ||||
| import { cloneDeep, formatDateTime, isEqual } from '@vben/utils'; | ||||
| 
 | ||||
| import { useDebounceFn } from '@vueuse/core'; | ||||
|  | @ -12,18 +12,25 @@ import { | |||
|   Button, | ||||
|   Card, | ||||
|   Collapse, | ||||
|   Dropdown, | ||||
|   Menu, | ||||
|   message, | ||||
|   Table, | ||||
|   Tag, | ||||
|   Tooltip, | ||||
| } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { deleteCategory } from '#/api/bpm/category'; | ||||
| import { updateModelSortBatch } from '#/api/bpm/model'; | ||||
| import { DictTag } from '#/components/dict-tag'; | ||||
| import { $t } from '#/locales'; | ||||
| import { DICT_TYPE } from '#/utils'; | ||||
| 
 | ||||
| // 导入重命名表单 | ||||
| import CategoryRenameForm from '../../category/modules/rename-form.vue'; | ||||
| 
 | ||||
| const props = defineProps<{ | ||||
|   categoryInfo: BpmCategoryApi.ModelCategoryInfo; | ||||
|   categoryInfo: ModelCategoryInfo; | ||||
|   isCategorySorting: boolean; | ||||
| }>(); | ||||
| 
 | ||||
|  | @ -131,6 +138,36 @@ const handleModelSortCancel = () => { | |||
|   } | ||||
| }; | ||||
| 
 | ||||
| /** 处理下拉菜单命令 */ | ||||
| const handleCommand = (command: string) => { | ||||
|   if (command === 'renameCategory') { | ||||
|     // 打开重命名分类对话框 | ||||
|     categoryRenameModalApi.setData(props.categoryInfo).open(); | ||||
|   } else if (command === 'deleteCategory') { | ||||
|     handleDeleteCategory(); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| /** 删除流程分类 */ | ||||
| const handleDeleteCategory = async () => { | ||||
|   if (props.categoryInfo.modelList.length > 0) { | ||||
|     message.warning('该分类下仍有流程定义,不允许删除'); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   confirm({ | ||||
|     content: `确定要删除[${props.categoryInfo.name}]吗?`, | ||||
|   }).then(async () => { | ||||
|     // 发起删除 | ||||
|     await deleteCategory(props.categoryInfo.id); | ||||
|     message.success( | ||||
|       $t('ui.actionMessage.deleteSuccess', [props.categoryInfo.name]), | ||||
|     ); | ||||
|     // 刷新列表 | ||||
|     emit('success'); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| /** 处理表单详情点击 */ | ||||
| const handleFormDetail = (row: any) => { | ||||
|   // TODO 待实现 | ||||
|  | @ -169,6 +206,17 @@ const customRow = (_record: any) => { | |||
|     class: isModelSorting.value ? 'cursor-move' : '', | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
| // 重命名分类对话框 | ||||
| const [CategoryRenameModal, categoryRenameModalApi] = useVbenModal({ | ||||
|   connectedComponent: CategoryRenameForm, | ||||
|   destroyOnClose: true, | ||||
| }); | ||||
| 
 | ||||
| // 处理重命名成功 | ||||
| const handleRenameSuccess = () => { | ||||
|   emit('success'); | ||||
| }; | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|  | @ -205,20 +253,39 @@ const customRow = (_record: any) => { | |||
| 
 | ||||
|         <div | ||||
|           class="ml-auto flex items-center" | ||||
|           :class="isModelSorting ? 'mr-4' : 'mr-12'" | ||||
|           :class="isModelSorting ? 'mr-4' : 'mr-8'" | ||||
|         > | ||||
|           <template v-if="!isModelSorting"> | ||||
|             <Button | ||||
|               v-if="categoryInfo.modelList.length > 0" | ||||
|               type="link" | ||||
|               class="mr-5 flex items-center text-[14px]" | ||||
|               size="small" | ||||
|               class="flex items-center text-[14px]" | ||||
|               @click.stop="handleModelSort" | ||||
|             > | ||||
|               <template #icon> | ||||
|                 <span class="icon-[fa--sort-amount-desc] mr-1"></span> | ||||
|                 <span class="icon-[fa--sort-amount-desc]"></span> | ||||
|               </template> | ||||
|               排序 | ||||
|             </Button> | ||||
|             <Dropdown placement="bottom" arrow> | ||||
|               <Button | ||||
|                 type="link" | ||||
|                 size="small" | ||||
|                 class="flex items-center text-[14px]" | ||||
|               > | ||||
|                 <template #icon> | ||||
|                   <span class="icon-[ant-design--setting-outlined]"></span> | ||||
|                 </template> | ||||
|                 分类 | ||||
|               </Button> | ||||
|               <template #overlay> | ||||
|                 <Menu @click="(e) => handleCommand(e.key as string)"> | ||||
|                   <Menu.Item key="renameCategory"> 重命名 </Menu.Item> | ||||
|                   <Menu.Item key="deleteCategory"> 删除分类 </Menu.Item> | ||||
|                 </Menu> | ||||
|               </template> | ||||
|             </Dropdown> | ||||
|           </template> | ||||
| 
 | ||||
|           <template v-else> | ||||
|  | @ -370,6 +437,9 @@ const customRow = (_record: any) => { | |||
|       </Collapse.Panel> | ||||
|     </Collapse> | ||||
|   </Card> | ||||
| 
 | ||||
|   <!-- 重命名分类弹窗 --> | ||||
|   <CategoryRenameModal @success="handleRenameSuccess" /> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 xingyu
						xingyu