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

Merge pull request !107 from Jason/dev
This commit is contained in:
xingyu
2025-05-19 02:32:32 +00:00
committed by Gitee
4 changed files with 216 additions and 31 deletions

View File

@@ -0,0 +1,160 @@
<script lang="ts" setup>
import type { DataNode } from 'ant-design-vue/es/tree';
import type { SystemDeptApi } from '#/api/system/dept';
import { defineProps, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { handleTree } from '@vben/utils';
import { Button, Card, Col, Row, Tree } from 'ant-design-vue';
import { getSimpleDeptList } from '#/api/system/dept';
defineOptions({ name: 'DeptSelectModal' });
const props = withDefaults(
defineProps<{
// 取消按钮文本
cancelText?: string;
// checkable 状态下节点选择完全受控
checkStrictly?: boolean;
// 确认按钮文本
confirmText?: string;
// 是否支持多选
multiple?: boolean;
// 标题
title?: string;
}>(),
{
cancelText: '取消',
checkStrictly: false,
confirmText: '确认',
multiple: true,
title: '部门选择',
},
);
const emit = defineEmits<{
confirm: [deptList: SystemDeptApi.Dept[]];
}>();
// 对话框配置
const [Modal, modalApi] = useVbenModal({
title: props.title,
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
resetData();
return;
}
modalApi.setState({ loading: true });
try {
deptData.value = await getSimpleDeptList();
deptTree.value = handleTree(deptData.value) as DataNode[];
} finally {
modalApi.setState({ loading: false });
}
},
destroyOnClose: true,
});
type checkedKeys = number[] | { checked: number[]; halfChecked: number[] };
// 部门树形结构
const deptTree = ref<DataNode[]>([]);
// 选中的部门 ID 列表
const selectedDeptIds = ref<checkedKeys>([]);
// 部门数据
const deptData = ref<SystemDeptApi.Dept[]>([]);
/** 打开对话框 */
const open = async (selectedList?: SystemDeptApi.Dept[]) => {
modalApi.open();
// // 设置已选择的部门
if (selectedList?.length) {
const selectedIds = selectedList
.map((dept) => dept.id)
.filter((id): id is number => id !== undefined);
selectedDeptIds.value = props.checkStrictly
? {
checked: selectedIds,
halfChecked: [],
}
: selectedIds;
}
};
/** 处理选中状态变化 */
const handleCheck = () => {
if (!props.multiple) {
// 单选模式下,只保留最后选择的节点
if (Array.isArray(selectedDeptIds.value)) {
const lastSelectedId =
selectedDeptIds.value[selectedDeptIds.value.length - 1];
if (lastSelectedId) {
selectedDeptIds.value = [lastSelectedId];
}
} else {
// checkStrictly 为 true 时selectedDeptIds 是一个对象
const checked = selectedDeptIds.value.checked || [];
if (checked.length > 0) {
const lastSelectedId = checked[checked.length - 1];
selectedDeptIds.value = {
checked: [lastSelectedId!],
halfChecked: [],
};
}
}
}
};
/** 提交选择 */
const handleConfirm = async () => {
// 获取选中的部门ID
const selectedIds: number[] = Array.isArray(selectedDeptIds.value)
? selectedDeptIds.value
: selectedDeptIds.value.checked || [];
const deptArray = deptData.value.filter((dept) =>
selectedIds.includes(dept.id!),
);
// 关闭并提示
await modalApi.close();
emit('confirm', deptArray);
};
const handleCancel = () => {
modalApi.close();
};
/** 重置数据 */
const resetData = () => {
deptTree.value = [];
selectedDeptIds.value = [];
};
/** 提供 open 方法,用于打开对话框 */
defineExpose({ open });
</script>
<template>
<Modal>
<Row class="h-full">
<Col :span="24">
<Card class="h-full">
<Tree
:tree-data="deptTree"
v-if="deptTree.length > 0"
v-model:checked-keys="selectedDeptIds"
:checkable="true"
:check-strictly="checkStrictly"
:field-names="{ title: 'name', key: 'id' }"
:default-expand-all="true"
@check="handleCheck"
/>
</Card>
</Col>
</Row>
<template #footer>
<Button @click="handleCancel">{{ cancelText }}</Button>
<Button type="primary" @click="handleConfirm">{{ confirmText }}</Button>
</template>
</Modal>
</template>

View File

@@ -0,0 +1 @@
export { default as DeptSelectModal } from './dept-select-modal.vue';

View File

@@ -12,7 +12,7 @@ import { useTabs } from '@vben/hooks';
import { ArrowLeft } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import { Button, message } from 'ant-design-vue';
import { Button, Card, message } from 'ant-design-vue';
import { getCategorySimpleList } from '#/api/bpm/category';
import { getProcessDefinition } from '#/api/bpm/definition';
@@ -467,27 +467,28 @@ onBeforeUnmount(() => {
</Button>
</div>
</div>
<!-- 主体内容 -->
<div class="mt-[50px]">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-[560px]">
<BasicInfo
v-model="formData"
:category-list="categoryList"
:user-list="userList"
:dept-list="deptList"
ref="basicInfoRef"
/>
<Card :body-style="{ padding: '10px' }" class="mb-4">
<div class="mt-[50px]">
<!-- 第一步基本信息 -->
<div v-if="currentStep === 0" class="mx-auto w-[560px]">
<BasicInfo
v-model="formData"
:category-list="categoryList"
:user-list="userList"
:dept-list="deptList"
ref="basicInfoRef"
/>
</div>
<!-- 第二步表单设计 TODO -->
<!-- 第三步流程设计 TODO -->
<!-- 第四步更多设置 TODO -->
<div v-show="currentStep === 3" class="mx-auto w-[700px]"></div>
</div>
<!-- 第二步表单设计 TODO -->
<!-- 第三步流程设计 TODO -->
<!-- 第四步更多设置 TODO -->
<div v-show="currentStep === 3" class="mx-auto w-[700px]"></div>
</div>
</Card>
</div>
</Page>
</template>

View File

@@ -2,6 +2,8 @@
import type { Rule } from 'ant-design-vue/es/form';
import type { SelectValue } from 'ant-design-vue/es/select';
import type { PropType } from 'vue';
import type { BpmCategoryApi } from '#/api/bpm/category';
import type { SystemDeptApi } from '#/api/system/dept';
import type { SystemUserApi } from '#/api/system/user';
@@ -20,18 +22,25 @@ import {
Tooltip,
} from 'ant-design-vue';
import { DeptSelectModal } from '#/components/dept-select-modal';
import { ImageUpload } from '#/components/upload';
import { UserSelectModal } from '#/components/user-select-modal';
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '#/utils';
const props = withDefaults(
defineProps<{
categoryList: BpmCategoryApi.CategoryVO[];
deptList: SystemDeptApi.Dept[];
userList: SystemUserApi.User[];
}>(),
{},
);
const props = defineProps({
categoryList: {
type: Array as PropType<BpmCategoryApi.CategoryVO[]>,
required: true,
},
userList: {
type: Array as PropType<SystemUserApi.User[]>,
required: true,
},
deptList: {
type: Array as PropType<SystemDeptApi.Dept[]>,
required: true,
},
});
// 表单引用
const formRef = ref();
@@ -45,6 +54,7 @@ const selectedStartDepts = ref<SystemDeptApi.Dept[]>([]);
// 选中的流程管理员
const selectedManagerUsers = ref<SystemUserApi.User[]>([]);
const userSelectFormRef = ref();
const deptSelectFormRef = ref();
const currentSelectType = ref<'manager' | 'start'>('start');
// 选中的用户
const selectedUsers = ref<number[]>();
@@ -99,8 +109,14 @@ const openStartUserSelect = () => {
/** 打开部门选择 */
const openStartDeptSelect = () => {
// TODO 部门选择组件暂时还没有
console.warn('部门选择功能暂未实现');
deptSelectFormRef.value.open(selectedStartDepts.value);
};
/** 处理部门选择确认 */
const handleDeptSelectConfirm = (depts: SystemDeptApi.Dept[]) => {
modelData.value = {
...modelData.value,
startDeptIds: depts.map((d) => d.id),
};
};
/** 打开管理员选择 */
@@ -343,7 +359,7 @@ defineExpose({
:key="dept.id"
class="relative flex h-9 items-center rounded-full bg-gray-100 pr-2"
>
<IconifyIcon icon="mdi:building-outline" class="size-5" />
<IconifyIcon icon="ep:office-building" class="size-6 px-1" />
{{ dept.name }}
<X
class="ml-2 size-4 cursor-pointer text-gray-400 hover:text-red-500"
@@ -408,6 +424,13 @@ defineExpose({
@closed="handleUserSelectClosed"
@cancel="handleUserSelectCancel"
/>
<!-- 部门选择对话框 -->
<DeptSelectModal
ref="deptSelectFormRef"
title="发起人部门选择"
:check-strictly="true"
@confirm="handleDeptSelectConfirm"
/>
</template>
<style lang="scss" scoped>