update 封装用户选择组件

This commit is contained in:
LiuHao 2024-01-23 02:46:58 +08:00
parent 4923f3c667
commit 917e661c5a
10 changed files with 363 additions and 37 deletions

View File

@ -2,7 +2,7 @@
<div ref="propertyPanel" class="property-panel">
<div v-if="nodeName">{{ nodeName }}</div>
<el-divider />
<component :is="component" v-if="element" :element="element" :modeler="modeler" :users="users" :groups="groups" :categorys="categorys" />
<component :is="component" v-if="element" :element="element" :modeler="modeler" :users="users" :groups="groups" />
</div>
</template>
<script setup lang="ts" name="PropertyPanel">
@ -16,7 +16,6 @@ import { Modeler, Modeling, Element } from 'bpmn';
interface propsType {
users: Array<any>;
groups: Array<any>;
categorys: Array<any>;
modeler: Modeler;
}
const props = withDefaults(defineProps<propsType>(), {});

View File

@ -1,19 +1,21 @@
import { Ref } from 'vue';
import { Element, Modeler } from 'bpmn';
interface Options {
interface Options<T> {
modeler: Modeler;
element: Element;
initData: T;
}
interface Data {
id: string;
}
export default <T extends Data>(ops: Options) => {
const { modeler, element } = ops;
export default <T extends Data>(ops: Options<T>) => {
const { modeler, element, initData } = ops;
const formData = ref<T>(initData);
const formData = ref<any>({});
const parse = () => {
const result = {
...element.businessObject,
@ -28,14 +30,7 @@ export default <T extends Data>(ops: Options) => {
delete result[key];
}
}
if ('documentation' in result) {
let str = '';
result.documentation.forEach((item: any) => {
str += item.text;
});
result.documentation = str;
}
formData.value = result;
formData.value = { ...formData.value, ...result };
return formData;
};

View File

@ -1,5 +1,5 @@
<template>
<el-dialog ref="flowDialogRef" v-model="dialog.visible" :title="dialog.title" width="95%" append-to-body @close="closeDialog">
<el-dialog ref="flowDialogRef" v-model="dialog.visible" :title="dialog.title" width="95%" @close="closeDialog">
<div class="app-containers">
<el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
<div class="flex pb-3 justify-between">
@ -62,6 +62,10 @@
</template>
<script lang="ts" setup name="BpmnDesign">
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import { Canvas, ElementRegistry, Modeler } from 'bpmn';
import PropertyPanel from './PropertyPanel.vue';
import BpmnModeler from 'bpmn-js/lib/Modeler.js';
@ -312,13 +316,9 @@ const getProcessElement = () => {
};
</script>
<style lang="scss">
<style lang="scss" scoped>
/*左边工具栏以及编辑节点的样式*/
//@import 'bpmn-js/dist/assets/bpmn-js.css';
@import 'bpmn-js/dist/assets/diagram-js.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
@import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
.app-containers {
width: 100%;
@ -342,15 +342,6 @@ const getProcessElement = () => {
}
}
.el-overlay {
.el-overlay-dialog {
.el-dialog {
.el-dialog__body {
padding-top: 0 !important;
}
}
}
}
.bpmn-icon-start-event-none:before {
//background-color: green;
//border-radius: 100%;

View File

@ -30,7 +30,8 @@ const props = withDefaults(defineProps<PropType>(), {});
const { parse, formData } = useParseElement<ProcessPanel>({
modeler: props.modeler,
element: toRaw(props.element)
element: toRaw(props.element),
initData: {} as any
});
const { idChange, nameChange } = usePanel({
modeler: props.modeler,

View File

@ -1,27 +1,46 @@
<template>
<div>
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
<el-form-item prop="id" label="节点 ID">
<el-input v-model="formData.id" @change="idChange"> </el-input>
</el-form-item>
<el-form-item prop="name" label="节点名称">
<el-input v-model="formData.name" @change="nameChange"> </el-input>
</el-form-item>
<el-form-item prop="userType" label="人员类型">
<el-input v-model="formData.userType"> </el-input>
<el-form-item prop="auditUserType" label="人员类型">
<el-select v-model="formData.auditUserType">
<el-option v-for="item in AuditUserTypeSelect" :key="item.id" :value="item.value" :label="item.label"> </el-option>
</el-select>
</el-form-item>
<el-form-item v-if="formData.auditUserType === AuditUserTypeEnum.USER" style="">
<el-button type="primary" @click="openUserSelect">选择人员</el-button>
</el-form-item>
<el-form-item v-if="formData.auditUserType === AuditUserTypeEnum.SPECIFY" style="">
<el-radio-group v-model="formData.specifyDesc" class="ml-4">
<el-radio v-for="item in SpecifyDesc" :key="item.id" :label="item.value" size="large">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="多人审批方式">
<el-radio-group v-model="formData.multipleUserAuditType" class="ml-4 block-radio">
<el-radio v-for="item in MultipleUserAuditType" :key="item.id" :label="item.value" size="large">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="执行监听器" style="margin-bottom: 0"> </el-form-item>
<ExecutionListener :modeler="modeler" :element="element"></ExecutionListener>
<el-form-item label="任务监听器" style="margin-bottom: 0"> </el-form-item>
<TaskListener :modeler="modeler" :element="element"></TaskListener>
</el-form>
<UserSelect ref="userSelectRef" v-model="formData.users"></UserSelect>
</div>
</template>
<script setup lang="ts">
import useParseElement from '@/components/BpmnDesign/hooks/useParseElement';
import usePanel from '@/components/BpmnDesign/hooks/usePanel';
import UserSelect from '@/components/UserSelect';
import { Element, Modeler } from 'bpmn';
import { TaskPanel } from 'bpmnDesign';
import { AuditUserTypeEnum, MultipleUserAuditTypeEnum } from '@/enums/bpmn/IndexEnums';
interface PropType {
modeler: Modeler;
@ -37,14 +56,47 @@ const { nameChange, idChange } = usePanel({
});
const { parse, formData } = useParseElement<TaskPanel>({
modeler: props.modeler,
element: toRaw(props.element)
element: toRaw(props.element),
initData: {
multipleUserAuditType: MultipleUserAuditTypeEnum.SERIAL,
auditUserType: AuditUserTypeEnum.USER,
users: []
} as any
});
const userSelectRef = ref<InstanceType<typeof UserSelect>>();
const formRules = ref<ElFormRules>({
processCategory: [{ required: true, message: '请选择', trigger: 'blur' }],
id: [{ required: true, message: '请输入', trigger: 'blur' }],
name: [{ required: true, message: '请输入', trigger: 'blur' }]
});
const AuditUserTypeSelect = [
{ id: 'b9cdf970-dd91-47c0-819f-42a7010ca2a6', label: '指定人员', value: 'user' },
{ id: '3f7ccbcd-c464-4602-bb9d-e96649d10585', label: '角色', value: 'role' },
{ id: 'c49065e0-7f2d-4c09-aedb-ab2d47d9a454', label: '发起人自己', value: 'yourself' },
{ id: '6ef40a03-7e9a-4898-89b2-c88fe9064542', label: '发起人指定', value: 'specify' }
];
const SpecifyDesc = [
{ id: 'fa253b34-4335-458c-b1bc-b039e2a2b7a6', label: '指定一个人', value: 'specifySingle' },
{ id: '7365ff54-2e05-4312-9bfb-0b8edd779c5b', label: '指定多个人', value: 'specifyMultiple' }
];
const MultipleUserAuditType = [
{ id: 'b5acea7c-b7e5-46b0-8778-390db091bdab', label: '串行(每人依次审批)', value: 'serial' },
{ id: 'b4f0c683-1ccc-43c4-8380-e1b998986caf', label: '并行(所有人审批通过)', value: 'parallel' },
{ id: '373d4b81-a0d1-4eb8-8685-0d2fb1b468e2', label: '或签(任意一人审批通过)', value: 'orSign' }
];
const openUserSelect = () => {
userSelectRef.value.open();
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.block-radio {
display: flex;
flex-flow: column nowrap;
align-items: flex-start;
}
</style>

View File

@ -18,6 +18,7 @@
@cell-dblclick="cellDBLClickEvent"
@menu-click="contextMenuClickEvent"
>
<vxe-column type="checkbox" width="40"></vxe-column>
<vxe-column type="seq" width="40"></vxe-column>
<vxe-column field="event" title="事件" min-width="100px">
<template #default="slotParams">

View File

@ -0,0 +1,262 @@
<template>
<div>
<el-dialog v-model="userDialog.visible.value" :title="userDialog.title.value" width="80%" append-to-body>
<div class="p-2">
<el-row :gutter="20">
<!-- 部门树 -->
<el-col :lg="4" :xs="24" style="">
<el-card shadow="hover">
<el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
<el-tree
ref="deptTreeRef"
class="mt-2"
node-key="id"
:data="deptOptions"
:props="{ label: 'label', children: 'children' }"
:expand-on-click-node="false"
:filter-node-method="filterNode"
highlight-current
default-expand-all
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
<el-col :lg="20" :xs="24">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
<el-form-item label="用户名称" prop="userName">
<el-input
v-model="queryParams.userName"
placeholder="请输入用户名称"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="手机号码" prop="phonenumber">
<el-input
v-model="queryParams.phonenumber"
placeholder="请输入手机号码"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
<el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" style="width: 308px">
<el-date-picker
v-model="dateRange"
value-format="YYYY-MM-DD"
type="daterange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus">新增</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
</el-row>
</template>
<vxe-table
ref="tableRef"
height="500px"
border
show-overflow
:data="userList"
:loading="loading"
:row-config="{ keyField: 'userId' }"
:checkbox-config="{ reserve: true, checkRowKeys: modelValue }"
highlight-current-row
>
<vxe-column type="checkbox" width="50" align="center" />
<vxe-column v-if="columns[0].visible" key="userId" title="用户编号" align="center" field="userId" />
<vxe-column v-if="columns[1].visible" key="userName" title="用户名称" align="center" field="userName" :show-overflow-tooltip="true" />
<vxe-column v-if="columns[2].visible" key="nickName" title="用户昵称" align="center" field="nickName" :show-overflow-tooltip="true" />
<vxe-column v-if="columns[3].visible" key="deptName" title="部门" align="center" field="deptName" :show-overflow-tooltip="true" />
<vxe-column v-if="columns[4].visible" key="phonenumber" title="手机号码" align="center" field="phonenumber" width="120" />
<vxe-column v-if="columns[5].visible" key="status" title="状态" align="center">
<template #default="scope">
<dict-tag :options="sys_normal_disable" :value="scope.row.status"></dict-tag>
</template>
</vxe-column>
<vxe-column v-if="columns[6].visible" title="创建时间" align="center" width="160">
<template #default="scope">
<span>{{ scope.row.createTime }}</span>
</template>
</vxe-column>
</vxe-table>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:total="total"
@pagination="getList"
/>
</el-card>
</el-col>
</el-row>
</div>
<template #footer>
<el-button @click="userDialog.closeDialog">取消</el-button>
<el-button type="primary" @click="confirm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import api from '@/api/system/user';
import { UserQuery, UserVO } from '@/api/system/user/types';
import { DeptVO } from '@/api/system/dept/types';
import { globalHeaders } from '@/utils/request';
import useDialog from '@/hooks/useDialog';
import { VxeTableInstance } from 'vxe-table';
interface PropType {
modelValue: string[];
}
const prop = defineProps<PropType>();
const emit = defineEmits(['update:modelValue']);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
const userList = ref<UserVO[]>();
const loading = ref(true);
const showSearch = ref(true);
const total = ref(0);
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
const deptName = ref('');
const deptOptions = ref<DeptVO[]>([]);
//
const columns = ref<FieldOption[]>([
{ key: 0, label: `用户编号`, visible: false, children: [] },
{ key: 1, label: `用户名称`, visible: true, children: [] },
{ key: 2, label: `用户昵称`, visible: true, children: [] },
{ key: 3, label: `部门`, visible: true, children: [] },
{ key: 4, label: `手机号码`, visible: true, children: [] },
{ key: 5, label: `状态`, visible: true, children: [] },
{ key: 6, label: `创建时间`, visible: true, children: [] }
]);
const deptTreeRef = ref<ElTreeInstance>();
const queryFormRef = ref<ElFormInstance>();
const tableRef = ref<VxeTableInstance<UserVO>>();
const userDialog = useDialog({
title: '用户选择'
});
const queryParams = ref<UserQuery>({
pageNum: 1,
pageSize: 10,
userName: '',
phonenumber: '',
status: '',
deptId: '',
roleId: ''
});
/** 通过条件过滤节点 */
const filterNode = (value: string, data: any) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
};
/** 根据名称筛选部门树 */
watchEffect(
() => {
deptTreeRef.value?.filter(deptName.value);
},
{
flush: 'post' // watchEffectDOMDOM
}
);
const confirm = () => {
const $table = tableRef.value;
if ($table) {
//
const checkboxRecords = $table.getCheckboxRecords();
//
const checkboxReserveRecords = $table.getCheckboxReserveRecords();
const data: UserVO[] = [...checkboxRecords, ...checkboxReserveRecords];
const userIds = data.map((item) => item.userId);
emit('update:modelValue', userIds);
userDialog.closeDialog();
}
};
/** 查询部门下拉树结构 */
const getTreeSelect = async () => {
const res = await api.deptTreeSelect();
deptOptions.value = res.data;
};
/** 查询用户列表 */
const getList = async () => {
loading.value = true;
const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
loading.value = false;
userList.value = res.rows;
total.value = res.total;
};
/** 节点单击事件 */
const handleNodeClick = (data: DeptVO) => {
queryParams.value.deptId = data.id;
handleQuery();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
dateRange.value = ['', ''];
queryFormRef.value?.resetFields();
queryParams.value.pageNum = 1;
queryParams.value.deptId = undefined;
deptTreeRef.value?.setCurrentKey(undefined);
handleQuery();
};
onMounted(() => {
getTreeSelect(); //
getList(); //
});
defineExpose({
open: userDialog.openDialog,
close: userDialog.closeDialog
});
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,17 @@
export enum AuditUserTypeEnum {
USER = 'user',
ROLE = 'role',
YOURSELF = 'yourself',
SPECIFY = 'specify'
}
export enum SpecifyDescEnum {
SPECIFY_MULTIPLE = 'specifyMultiple',
SPECIFY_SINGLE = 'specifySingle'
}
export enum MultipleUserAuditTypeEnum {
SERIAL = 'serial',
PARALLEL = 'parallel',
OR_SIGN = 'orSign'
}

View File

@ -1,4 +1,6 @@
declare module 'bpmnDesign' {
import { AuditUserTypeEnum, SpecifyDescEnum, MultipleUserAuditTypeEnum } from '@/enums/bpmn/IndexEnums';
export interface ParamVO {
type: string;
name: string;
@ -30,7 +32,11 @@ declare module 'bpmnDesign' {
export interface TaskPanel extends BasePanel {
processCategory: string;
userType: string;
auditUserType: AuditUserTypeEnum;
specifyDesc: SpecifyDescEnum;
multipleUserAuditType: MultipleUserAuditTypeEnum;
users: string[];
assignee: string;
candidateUsers: string;
candidateGroups: string;

View File

@ -70,6 +70,8 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
'min-dash',
'bpmn-js/lib/features/palette/PaletteProvider',
'bpmn-js/lib/features/context-pad/ContextPadProvider',
'diagram-js/lib/draw/BaseRenderer',
'tiny-svg',
'element-plus/es/components/container/style/css',
'element-plus/es/components/aside/style/css',