feat: 优化选人组件和选择组织组件

This commit is contained in:
gaoyunqi
2024-05-09 09:42:56 +08:00
parent a876a6694c
commit 71cc53ec8a
12 changed files with 937 additions and 6 deletions

View File

@ -10,6 +10,10 @@
- 设计器支持响应式布局,因为设计器架构问题,并未默认打开,对于表单内字段,除了附件、多行文本框等占用宽度较大的组件外,都建议开启响应式布局
- 表单和表格需要合理调整字段宽度,响应式布局下需要使用定宽模式,一般情况下,字段的宽度取平均字长 + 2个汉字的宽度为宜不要留太长的label也要避免出现label换行
## Q&A
### 为什么表单所有字段都成了必填
默认情况下,绑定流程后,新建节点的所有字段都被设置为必填,需要在流程的开始节点-表单设置中去掉非必填的项。
## 在Tab页中打开表单/流程
如果你需要自己编程实现Tab页跳转或者升级旧版框架的页面可以参考下面步骤
```typescript
@ -63,7 +67,7 @@ function dbClickRow(record) {
}
}
```
同时,因为外层封装页面需要表单加载后的元数据,需要通过事件将表单数据传出。
同时,因为外层封装页面需要表单加载后的元数据,需要在Form.vue中通过事件将表单数据传出。
```javascript
// 这行是原来有的
import { formProps, formEventConfigs } from './config';
@ -146,3 +150,20 @@ export const formProps: FormProps = {
</template>
```
注意根据antd-vue的文档bodyCell里除了要修改的列不能写其他内容否则会将内容覆盖到所有列上
## 如何在按钮栏中刚增加按钮
## 如何修改选项卡标题
```javascript
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
import { useRouter } from 'vue-router';
const tabStore = useMultipleTabStore();
const router = useRouter();
const currentRoute = router.currentRoute.value;
const fullPath = currentRoute.fullPath;
tabStore.changeTitle(fullPath, `选项卡标题`);
// 顺便tabStore也支持关闭选项卡
tabStore.closeTab(currentRoute, router);
```

View File

@ -12,6 +12,7 @@ import {
enum Api {
Page = '/organization/department/page',
Tree = '/organization/department/tree',
Trees = '/organization/department/trees',
EnabledTree = '/organization/department/enabled-tree',
Info = '/organization/department/info',
Department = '/organization/department',
@ -23,7 +24,7 @@ enum Api {
* @description: 查询部门树
*/
export async function getDepartmentTree(
params?: DepartmentTreeParams,
params?: any,
mode: ErrorMessageMode = 'modal',
) {
return defHttp.get<DepartmentTreeModel>(
@ -36,6 +37,23 @@ export async function getDepartmentTree(
},
);
}
/**
* @description: 查询部门树(新)
*/
export async function getDepartmentTrees(
params?: any,
mode: ErrorMessageMode = 'modal',
) {
return defHttp.get<DepartmentTreeModel>(
{
url: Api.Trees,
params,
},
{
errorMessageMode: mode,
},
);
}
/**
* @description: 查询部门树

View File

@ -49,6 +49,23 @@ export async function getUserPageList(
},
);
}
/**
* @description: 查询用户分页列表(新)
*/
export async function getUserPageListNew(
params: any,
mode: ErrorMessageMode = 'modal',
) {
return defHttp.get<UserPageListResultModel>(
{
url: Api.Page,
params,
},
{
errorMessageMode: mode,
},
);
}
/**
* @description: 删除用户(批量删除)

View File

@ -756,7 +756,7 @@
import ApiCheckboxGroup from '/@/components/Form/src/components/ApiCheckboxGroup.vue';
import SelectArea from '/@/components/Form/src/components/SelectArea.vue';
import CommonInfo from '/@/components/Form/src/components/CommonInfo.vue';
import SelectDepartment from '/@/components/Form/src/components/SelectDepartment.vue';
import SelectDepartment from '/@/components/Form/src/components/SelectDepartmentV2.vue';
import SelectMap from '/@/components/Form/src/components/SelectMap.vue';
import XjrQrcode from '/@/components/Form/src/components/QrCode.vue';
import ApiComplete from '/@/components/Form/src/components/ApiComplete.vue';

View File

@ -455,6 +455,11 @@
<a-form-item v-if="hasKey('reverse')" :label="t('反向坐标轴')">
<a-switch v-model:checked="data.options.reverse" />
</a-form-item>
<template v-if="data.type === 'organization'">
<a-form-item v-if="hasKey('parentNode')" label="父级节点">
<SelectDepartmentV2 v-model:value="data.options.parentNode"></SelectDepartmentV2>
</a-form-item>
</template>
<template v-if="data.type === 'image'">
<a-form-item :label="t('默认值')">
@ -756,6 +761,7 @@
// import { getAppEnvConfig } from '/@/utils/env';
import { ColorPicker } from '/@/components/ColorPicker';
import { TimePicker } from '/@/components/TimePicker';
import SelectDepartmentV2 from '/@/components/Form/src/components/SelectDepartmentV2.vue';
import { XjrDatePicker } from '/@/components/DatePicker';
import { DicTreeSelect } from '/@/components/DicTreeSelect';
import Sortable from 'sortablejs';
@ -785,7 +791,8 @@
XjrDatePicker,
DicTreeSelect,
ApiAssoConfig,
DicAssoConfig
DicAssoConfig,
SelectDepartmentV2
},
props: {
//所选组件配置

View File

@ -1162,6 +1162,7 @@ export const infoComponents = [
options: {
labelWidthMode: 'fix',
labelFixWidth: 120,
parentNode: '',
responsive: false,
span: '',
width: '100%',

View File

@ -21,7 +21,9 @@ import ApiTree from './components/ApiTree.vue';
import ApiTreeSelect from './components/ApiTreeSelect.vue';
import ApiCascader from './components/ApiCascader.vue';
import SelectDepartment from './components/SelectDepartment.vue';
import SelectDepartmentV2 from './components/SelectDepartmentV2.vue';
import SelectUser from './components/SelectUser.vue';
import SelectUserV2 from './components/SelectUserV2.vue';
import CommonInfo from './components/CommonInfo.vue';
import SelectArea from './components/SelectArea.vue';
import AutoCodeRule from './components/AutoCodeRule.vue';
@ -97,8 +99,10 @@ componentMap.set('CheckboxGroup', Checkbox.Group);
componentMap.set('ApiCascader', ApiCascader);
componentMap.set('Slider', Slider);
componentMap.set('Rate', Rate);
componentMap.set('Dept', SelectDepartment);
componentMap.set('User', SelectUser);
componentMap.set('Dept', SelectDepartmentV2);
componentMap.set('DeptV2', SelectDepartmentV2);
componentMap.set('User', SelectUserV2);
componentMap.set('UserV2', SelectUserV2);
componentMap.set('Info', CommonInfo);
componentMap.set('Area', SelectArea);
componentMap.set('SubForm', SubForm);

View File

@ -0,0 +1,102 @@
<template>
<div class="select-department-tree">
<a-tree v-model:expandedKeys="expandedKeys" v-model:selectedKeys="selectedKeys" v-model:checkedKeys="checkedKeys"
:selectable="props.selectable" :multiple="props.multiple" v-model:checkable="checkable"
v-model:tree-data="treeData" :load-data="loadData" @select="selectData">
<template #title="{ title, key }">
<span v-if="key === '0-0-1-0'" style="color: #1890ff">{{ title }}</span>
<template v-else>{{ title }}</template>
</template>
</a-tree>
</div>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from 'vue';
import { getDepartmentTrees } from '/@/api/system/department';
const props = defineProps({
multiple: {
type: Boolean,
default: false
},
selectable: {
type: Boolean,
default: true
},
value: {
type: String,
default: ''
},
parentNode: {
type: String,
default: ''
},
});
const emit = defineEmits(['change-value', 'query-completed']);
const expandedKeys = ref<string[]>([]);
const selectedKeys = ref<string[]>([]);
const checkedKeys = ref<string[]>([]);
const checkable = ref(false)
const treeData = ref([])
const params = ref({
id: '',
code: '',
name: '',
parentNode: false
})
onMounted(() => {
selectedKeys.value = [props.value]
getList()
})
const loadData = (node) => {
return new Promise(async (resolve: (value?: unknown) => void) => {
if (node.dataRef.children.length) {
resolve();
return;
}
let param = {
id: node.id,
code: '',
name: '',
parentNode: false
}
let list = resetTreeList(await getDepartmentTrees(param))
node.dataRef.children = list[0].children
treeData.value = [...treeData.value];
resolve();
});
}
async function getList() {
if (props.parentNode) {
params.value.id = props.parentNode
}
let list = resetTreeList(await getDepartmentTrees(params.value))
treeData.value = list
emit('query-completed');
}
function resetTreeList(list) {
const result = list.map(item => {
return {
...item,
...{
key: item.id,
children: resetTreeList(item.children),
title: item.name
}
}
})
return result
}
function selectData(selectedKeys, e) {
emit('change-value', e.selectedNodes);
}
watch(expandedKeys, () => {
console.log('expandedKeys', expandedKeys);
});
watch(selectedKeys, () => {
console.log('selectedKeys', selectedKeys);
});
watch(checkedKeys, () => {
console.log('checkedKeys', checkedKeys);
});
</script>

View File

@ -0,0 +1,199 @@
<template>
<div style="width: 100%" @click="show">
<a-input v-model:value="departNames" :bordered="bordered" :disabled="disabled" :placeholder="placeholder"
:size="size" readonly>
<template v-if="prefix" #prefix>
<Icon :icon="prefix" />
</template>
<template v-if="suffix" #suffix>
<Icon :icon="suffix" />
</template>
</a-input>
<ModalPanel :visible="visible" :width="800" :title="t('添加部门')" @submit="submit" @close="close">
<div class="choose-dep-box">
<div class="choose-dep">
<a-spin class="loading-box" :spinning="loading" />
<SelectDepartmentTreeV2 v-if="visible" @changeValue="departChange" :value="props.value"
@queryCompleted="queryCompleted" :parentNode="parentNode"></SelectDepartmentTreeV2>
</div>
<div class="choosen-dep">
<div class="choosen-item" v-for="item in selectedNodes">
<div class="choosen-label">{{ item.name }}</div>
<close-outlined class="close" @click="deleteItem" />
</div>
</div>
</div>
</ModalPanel>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue';
import SelectDepartmentTreeV2 from './SelectDepartmentTreeV2.vue'
import { useI18n } from '/@/hooks/web/useI18n';
import { ModalPanel } from '/@/components/ModalPanel/index';
const { t } = useI18n();
const emits = defineEmits(['change', 'changeNames', 'close', 'options-change', 'update:value']);
import { getDepartmentTrees } from '/@/api/system/department';
import { CloseOutlined } from '@ant-design/icons-vue';
const visible = ref(false)
const departNames = ref<string>();
const props = defineProps({
value: String,
prefix: String,
suffix: String,
placeholder: String,
readonly: Boolean,
disabled: Boolean,
parentNode: String,
size: String,
bordered: {
type: Boolean,
default: true
},
multiple: {
type: Boolean,
default: true
},
componentProps: {
type: Object,
default: {}
}
});
const selectedNodes = ref([])
const loading = ref(true)
// Embedded in the form, just use the hook binding to perform form verification
onMounted(() => {
});
watch(
() => props.value,
(val: any) => {
if (val) {
getDefaultList(val)
}
},
{
immediate: true,
},
);
async function getDefaultList(id) {
let param = {
id: id,
code: '',
name: '',
parentNode: false
}
let list = resetTreeList(await getDepartmentTrees(param))
selectedNodes.value = list
let nameList = selectedNodes.value.map(item => item.name)
const names = nameList.join(',')
departNames.value = names;
// handleSelectedNodes()
}
function resetTreeList(list) {
const result = list.map(item => {
return {
...item,
...{
key: item.id,
children: resetTreeList(item.children),
title: item.name
}
}
})
return result
}
function queryCompleted() {
loading.value = false
}
function departChange(nodes) {
selectedNodes.value = nodes
}
function show() {
visible.value = true
loading.value = true
if (props.value) {
getDefaultList(props.value)
}
}
function handleSelectedNodes() {
let nameList = selectedNodes.value.map(item => item.name)
let idList = selectedNodes.value.map(item => item.id)
const names = nameList.join(',')
const ids = idList.join(',')
emits('update:value', ids);
departNames.value = names;
}
function deleteItem() {
selectedNodes.value = []
}
function submit() {
handleSelectedNodes()
close();
}
function close() {
visible.value = false;
selectedNodes.value = []
emits('close');
}
</script>
<style lang="less" scoped>
.choose-dep-box {
display: flex;
height: 95%;
.choose-dep {
border: 1px solid #eaeaea;
margin: 8px;
width: 60%;
height: 100%;
position: relative;
overflow: scroll;
.loading-box {
position: absolute;
left: 49%;
top: 49%;
}
}
.choosen-dep {
border: 1px solid #eaeaea;
margin: 8px;
width: 40%;
height: 100%;
padding: 4px;
.choosen-item {
padding: 2px 4px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.choosen-label {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.close {
visibility: hidden
}
}
.choosen-item:hover {
background-color: #eaeaea;
.close {
visibility: visible
}
}
}
}
</style>

View File

@ -0,0 +1,137 @@
<template>
<div class="user-select-list">
<div class="user-select-item" v-for="item in data" @click="selectItem(item)">
<div class="user-select-item-left">
{{ item.name }}
</div>
<div class="user-select-item-right">
<div class="select-circle" :class="item.selected ? 'selected' : ''" v-if="multiple && !viewList">
<check-outlined v-if="item.selected" />
</div>
<div class="delete-circle" v-if="canDel" @click="delItem(item)">
<CloseOutlined />
</div>
</div>
</div>
<div class="empty-box" v-if="!data.length">
<a-empty :image="simpleImage">
<template #description>
<div v-html="emptyDescription"></div>
</template>
</a-empty>
</div>
</div>
</template>
<script lang="ts" setup>
import { watch, ref } from 'vue';
import { CheckOutlined, CloseOutlined } from '@ant-design/icons-vue';
import { Empty } from 'ant-design-vue';
import { string } from 'vue-types';
const emits = defineEmits(['update:value', 'selectId', 'change', 'delId']);
const simpleImage = ref(Empty.PRESENTED_IMAGE_SIMPLE)
const props = defineProps({
multiple: {
type: Boolean,
default: true
},
emptyDescription: {
type: String,
default: '暂无数据'
},
data: {
type: Array,
default: []
},
canDel: {
type: Boolean,
default: false
},
viewList: {
type: Boolean,
default: false
},
})
function selectItem(item) {
emits('selectId', item.id)
}
function delItem(item) {
emits('delId', item.id)
}
</script>
<style lang="less" scoped>
.user-select-list {
flex: 1;
overflow-y: auto;
padding: 4px;
.empty-box {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
:deep(.ant-empty-normal) {
// margin: auto;
}
}
.user-select-item {
height: 30px;
padding: 0 10px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 2px;
margin: 5px 0;
color: #111111;
.user-select-item-left {
display: flex;
align-items: center;
}
.user-select-item-right {
display: flex;
align-items: center;
.select-circle {
width: 16px;
height: 16px;
border: 1px solid rgba(144, 147, 153, 0.7);
border-radius: 50%;
}
.delete-circle {
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
cursor: pointer;
visibility: hidden;
}
.selected {
border: none;
background-color: #5d9cec;
display: flex;
justify-content: center;
align-items: center;
color: white;
font-size: 10px
}
}
}
.user-select-item:hover {
background-color: #f6f6f6;
.delete-circle {
visibility: visible;
}
}
}
</style>

View File

@ -0,0 +1,423 @@
<template>
<div :class="{ disabled }" class="form-select-user" @click="show">
<a-input v-model:value="userNames" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
<template v-if="prefix" #prefix>
<Icon :icon="prefix" />
</template>
<template v-if="suffix" #suffix>
<Icon :icon="suffix" />
</template>
</a-input>
<ModalPanel :visible="visible" :width="900" :title="t('选择人员')" @submit="submit" @close="close"
class="select-user-model">
<div class="select-user">
<div class="select-user-left">
<a-tabs v-model:activeKey="activeKey" :tabBarGutter="20">
<a-tab-pane key="department" tab="组织架构">
</a-tab-pane>
<a-tab-pane key="allPerson" tab="所有人">
</a-tab-pane>
</a-tabs>
<div class="person-search">
<a-form-item label="" name="name">
<a-input v-model:value="selectPersonNames" :placeholder="searchPlaceholder" :size="size"
class="search-input">
</a-input>
</a-form-item>
</div>
<div class="select-user-box" v-show="activeKey === 'department'">
<div class="department-tree">
<a-spin class="loading-box" :spinning="treeLoading" />
<div class="department-tree-box">
<!-- <div class="department-tree-title sub-title">
组织
</div> -->
<SelectDepartmentTreeV2 @changeValue="departChange" class="tree-select"
v-if="visible && activeKey === 'department'" @queryCompleted="departCompleted">
</SelectDepartmentTreeV2>
</div>
</div>
<div class="user-select-box">
<!-- <div class="user-select-title sub-title">
可选列表
</div> -->
<SelectUserListV2 :data="searchDepartMemberList" @selectId="changeDepMemberSelect"
emptyDescription="暂无人员">
</SelectUserListV2>
<div class="user-select-pagination" v-if="false">
<a-pagination v-model:current="searchDepartMemberParams.limit" :pageSize="searchDepartMemberParams.size"
:total="searchDepartMemberTotal" />
</div>
</div>
</div>
<div class="all-user-select-box" v-show="activeKey === 'allPerson'">
<!-- <div class="all-user-select-title sub-title">
可选列表
</div> -->
<SelectUserListV2 :data="searchAllMemberList" @selectId="changeMemberSelect"></SelectUserListV2>
<div class="all-user-select-pagination" v-if="searchAllMemberTotal > 25">
<a-form-item label="" name="pagination">
<a-pagination size="small" showLessItems :total="searchAllMemberTotal" :show-size-changer="false"
v-model:current="searchAllMemberParams.limit" :pageSize="searchAllMemberParams.size"
@change="changeAllMemberPage" hideOnSinglePage :show-total="total => `${total}`" />
</a-form-item>
</div>
</div>
</div>
<div class="select-user-right">
<!-- <div class="selected-user-title sub-title">
已选列表
</div> -->
<SelectUserListV2 :data="selectedMemberList" viewList canDel @delId="delMember"
emptyDescription="暂无已选择人员<br> 请从左侧添加人员">
</SelectUserListV2>
</div>
</div>
</ModalPanel>
</div>
</template>
<script lang="ts" setup>
import { watch, ref } from 'vue';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { ModalPanel } from '/@/components/ModalPanel/index';
import { getUserPageListNew, getUserMulti } from '/@/api/system/user';
import SelectDepartmentTreeV2 from './SelectDepartmentTreeV2.vue'
import SelectUserListV2 from './SelectUserListV2.vue';
const emits = defineEmits(['update:value', 'selectedId', 'change']);
const { t } = useI18n();
const userNames = ref('')
const treeLoading = ref(true)
const visible = ref(false)
const activeKey = ref('department')
const selectedMemberList = ref([])
const selectPersonNames = ref('')
const props = defineProps({
value: {
type: String,
default: ''
},
prefix: String,
suffix: String,
placeholder: String,
readonly: Boolean,
disabled: Boolean,
size: String,
multiple: {
type: Boolean,
default: true
}
});
let timeoutId = null;
let resetMemberList = []
const searchPlaceholder = '请输入姓名搜索'
// 防止输入多次调用搜索接口导致问题,使用防抖
watch(selectPersonNames, (newValue) => {
if (newValue && activeKey.value !== 'allPerson') {
activeKey.value = 'allPerson'
}
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
// 调用搜索接口
console.log('执行搜索API操作, 查询参数为:', newValue);
searchNameChanged(newValue)
}, 1000);
});
watch(activeKey, (newValue) => {
searchAllMemberList.value = []
searchAllMemberTotal.value = 0
searchDepartMemberList.value = []
treeLoading.value = true
if (newValue !== 'allPerson') {
selectPersonNames.value = ''
} else {
if (!timeoutId) {
searchNameChanged('')
}
}
})
watch(props, async () => {
if (props.value && !resetMemberList.length) {
const list = await getUserMulti(props.value)
selectedMemberList.value = list
resetMemberList = cloneDeep(list)
const nameList = resetMemberList.map(item => {
return item.name
})
userNames.value = nameList.join(',')
}
}, {
immediate: true
})
const searchDepartMemberParams = ref({
limit: 1,
size: 10000,
departmentId: ''
})
const searchDepartMemberTotal = ref(0)
const searchDepartMemberList = ref()
const searchAllMemberParams = ref({
limit: 1,
size: 25,
isSearchAll: true,
name: ''
})
const searchAllMemberTotal = ref(0)
const searchAllMemberList = ref()
async function departChange(e) {
searchDepartMemberParams.value.departmentId = e[0].id
let res = await getUserList(searchDepartMemberParams.value)
searchDepartMemberTotal.value = res.total
searchDepartMemberList.value = setSelected(res.list)
}
async function searchNameChanged(val) {
searchAllMemberParams.value.limit = 1
searchAllMemberParams.value.name = val
getAllMemberList()
}
async function getAllMemberList() {
let res = await getUserList(searchAllMemberParams.value)
searchAllMemberTotal.value = res.total
searchAllMemberList.value = setSelected(res.list)
}
function setSelected(list) {
const idList = selectedMemberList.value.map(item => item.id)
const ids = idList.join(',')
list.forEach(item => {
if (ids?.includes(item.id)) {
item.selected = true
} else {
item.selected = false
}
})
return list
}
async function getUserList(params) {
return await getUserPageListNew(params)
}
function departCompleted() {
treeLoading.value = false
}
function changeDepMemberSelect(id) {
let list = searchDepartMemberList.value
searchDepartMemberList.value = handleListSelected(list, id)
}
function changeMemberSelect(id) {
let list = searchAllMemberList.value
searchAllMemberList.value = handleListSelected(list, id)
}
function handleListSelected(list, id) {
let selected = false
let selectedItem
list.forEach(item => {
if (item.id === id) {
item.selected = !!!item.selected
selected = item.selected
selectedItem = item
}
})
let memberList = selectedMemberList.value
if (selected) {
memberList.push(selectedItem)
selectedMemberList.value = memberList
} else {
selectedMemberList.value = memberList.filter(m => m.id !== id)
}
return list
}
function changeAllMemberPage(page) {
searchAllMemberParams.value.limit = page
getAllMemberList()
}
function show() {
visible.value = true
treeLoading.value = true
}
function delMember(id) {
if (activeKey.value === 'department') {
changeDepMemberSelect(id)
} else {
changeMemberSelect(id)
}
}
function submit() {
let idsList = []
let namesList = []
selectedMemberList.value.forEach(item => {
idsList.push(item.id)
namesList.push(item.name)
})
const ids = idsList.join(',')
const names = namesList.join(',')
resetMemberList = cloneDeep(selectedMemberList.value)
userNames.value = names
emits('update:value', ids);
emits('selectedId', ids);
emits('change', ids, selectedMemberList.value);
close();
}
function close() {
selectedMemberList.value = cloneDeep(resetMemberList)
visible.value = false;
activeKey.value = 'department'
searchDepartMemberList.value = []
searchAllMemberList.value = []
}
</script>
<style lang="less">
.select-user-model {
.content {
margin: 0 !important;
}
}
</style>
<style lang="less" scoped>
.sub-title {
height: 30px;
padding: 0 8px;
display: flex;
align-items: center;
justify-content: space-between;
color: rgba(144, 147, 153, 0.7);
}
.select-user {
display: flex;
height: 100%;
// border: 1px solid #eaeaea;
.select-user-left {
width: 75%;
height: 100%;
border-right: 1px solid #eaeaea;
display: flex;
flex-direction: column;
:deep(.ant-tabs-nav) {
margin-bottom: 0;
.ant-tabs-nav-wrap {
padding-left: 10px;
}
}
.person-search {
padding: 4px;
:deep(.ant-form-item) {
margin: 0;
}
.search-input {
border: none;
}
}
.select-user-box {
flex: 1;
min-height: 0;
display: flex;
border-top: 1px solid #eaeaea;
.department-tree {
position: relative;
width: 65%;
height: 100%;
border-right: 1px solid #eaeaea;
.loading-box {
position: absolute;
top: 49%;
left: 49%;
}
.department-tree-box {
// overflow: scroll;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
padding: 4px 8px;
.department-tree-title {}
.tree-select {
flex: 1;
min-height: 0;
overflow-y: scroll;
}
}
}
.user-select-box {
flex: 1;
min-height: 0;
height: 100%;
display: flex;
flex-direction: column;
.user-select-title {}
.user-select-list {
flex: 1;
min-height: 0;
overflow-y: scroll;
.user-select-item {
height: 30px;
padding: 0 4px;
}
}
.user-select-pagination {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
}
}
.all-user-select-box {
flex: 1;
min-height: 0;
height: 100%;
display: flex;
flex-direction: column;
.all-user-select-title {}
.all-user-select-pagination {
height: 40px;
display: flex;
justify-content: center;
align-items: center;
.ant-form-item {
margin-bottom: 0;
}
}
}
}
.select-user-right {
width: 25%;
height: 100%;
display: flex;
flex-direction: column;
.selected-user-title {}
}
}
</style>

View File

@ -125,7 +125,9 @@ export type ComponentType =
| 'ChildTable'
| 'Divider'
| 'Dept'
| 'DeptV2'
| 'User'
| 'UserV2'
| 'Info'
| 'Area'
| 'SubForm'