初始版本提交
This commit is contained in:
17
src/components/SelectOrganizational/index.ts
Normal file
17
src/components/SelectOrganizational/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
|
||||
import organizationalTree from './src/OrganizationalTree.vue';
|
||||
import userCard from './src/card/UserCard.vue';
|
||||
import selectUser from './src/SelectUser.vue';
|
||||
import selectPost from './src/SelectPost.vue';
|
||||
import selectRole from './src/SelectRole.vue';
|
||||
import selectMember from './src/SelectMember.vue';
|
||||
import selectDepartment from './src/SelectDepartment.vue';
|
||||
|
||||
export const OrganizationalTree = withInstall(organizationalTree);
|
||||
export const SelectUser = withInstall(selectUser);
|
||||
export const SelectPost = withInstall(selectPost);
|
||||
export const SelectRole = withInstall(selectRole);
|
||||
export const UserCard = withInstall(userCard);
|
||||
export const SelectMember = withInstall(selectMember);
|
||||
export const SelectDepartment = withInstall(selectDepartment);
|
||||
@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="overflow-hidden">
|
||||
<NodeHead class="header-title" :node-name="t('组织架构')" />
|
||||
<BasicTree
|
||||
title=""
|
||||
search
|
||||
expandOnSearch
|
||||
:checkable="isCheckable"
|
||||
:treeData="treeData"
|
||||
:fieldNames="{ key: 'id', title: 'name' }"
|
||||
@select="handleSelect"
|
||||
@check="handleCheck"
|
||||
>
|
||||
<template #title="{ name, departmentType }">
|
||||
<a-tag color="processing" v-if="departmentType === 1">公司</a-tag>
|
||||
<a-tag color="warning" v-else-if="departmentType === 0">部门</a-tag>
|
||||
{{ name }}
|
||||
</template>
|
||||
</BasicTree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getDepartmentTree } from '/@/api/system/department';
|
||||
import { TreeItem } from '/@/components/Tree';
|
||||
import { BasicTree } from '/@/components/Tree';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
|
||||
defineProps({
|
||||
isCheckable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['select', 'check']);
|
||||
const treeData = ref<TreeItem[]>([]);
|
||||
async function getList() {
|
||||
treeData.value = (await getDepartmentTree()) as unknown as TreeItem[];
|
||||
}
|
||||
function handleSelect(keys: string) {
|
||||
emits('select', keys[0]);
|
||||
}
|
||||
|
||||
function handleCheck(keys: string) {
|
||||
emits('check', keys);
|
||||
}
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.header-title {
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-treenode) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
:deep(.vben-tree-header) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
281
src/components/SelectOrganizational/src/SelectDepartment.vue
Normal file
281
src/components/SelectOrganizational/src/SelectDepartment.vue
Normal file
@ -0,0 +1,281 @@
|
||||
<template>
|
||||
<ModalPanel
|
||||
:visible="props.visible"
|
||||
:width="1200"
|
||||
:isDeptSelect="data.isDeptSelect"
|
||||
:title="t('添加人员')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<template #header>
|
||||
<a-row class="header-box">
|
||||
<a-col :span="2" align="right"> <span class="required-dot">*</span>选择类型: </a-col>
|
||||
<a-col :span="22">
|
||||
<a-select v-model:value="data.selectType" @change="handleTypeChange" style="width: 100%">
|
||||
<a-select-option :value="0">选择人员</a-select-option>
|
||||
<a-select-option :value="1">按组织批量选择</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<template #left>
|
||||
<OrganizationalTree
|
||||
@select="handleSelect"
|
||||
@check="handleCheck"
|
||||
:isCheckable="data.isDeptSelect"
|
||||
/>
|
||||
</template>
|
||||
<div v-if="!data.isDeptSelect">
|
||||
<!-- 已选 -->
|
||||
<Selected
|
||||
v-if="visible"
|
||||
type="user"
|
||||
:list="data.selectedList"
|
||||
@abolish="abolishChecked"
|
||||
:disabledIds="props.disabledIds"
|
||||
/>
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-page-box" v-if="visible && data.list.length > 0">
|
||||
<UserCard
|
||||
:class="data.selectedIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
:disabled="props.disabledIds && props.disabledIds.includes(item.id) ? true : false"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="data.selectedIds.includes(item.id)" />
|
||||
</template>
|
||||
</UserCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="data.page.current"
|
||||
:pageSize="data.page.pageSize"
|
||||
:total="data.page.total"
|
||||
show-less-items
|
||||
/></div>
|
||||
</div>
|
||||
<EmptyBox v-if="data.list.length == 0" />
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, watch } from 'vue';
|
||||
import OrganizationalTree from '/@/components/SelectOrganizational/src/OrganizationalTree.vue';
|
||||
import UserCard from '/@/components/SelectOrganizational/src/card/UserCard.vue';
|
||||
import Selected from '/@/components/SelectOrganizational/src/Selected.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
import { getUserList, getUserMulti } from '/@/api/system/user';
|
||||
import { UserInfo } from '/@/api/system/user/model';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change', 'changeNames', 'close']);
|
||||
|
||||
const props = defineProps({
|
||||
selectedIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
disabledIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
multiple: Boolean,
|
||||
visible: Boolean,
|
||||
});
|
||||
|
||||
let data: {
|
||||
multiSelect: boolean;
|
||||
page: { current: number; total: number; pageSize: number };
|
||||
list: Array<UserInfo>;
|
||||
selectedList: Array<UserInfo>;
|
||||
selectedIds: Array<string>;
|
||||
searchConfig: {
|
||||
keyword: string;
|
||||
deptId: string;
|
||||
};
|
||||
selectType: number;
|
||||
isDeptSelect: boolean;
|
||||
departmentIds: Array<string>;
|
||||
} = reactive({
|
||||
multiSelect: false,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 9,
|
||||
},
|
||||
selectedIds: [],
|
||||
list: [],
|
||||
selectedList: [],
|
||||
searchConfig: {
|
||||
keyword: '',
|
||||
deptId: '',
|
||||
},
|
||||
selectType: 0,
|
||||
isDeptSelect: false,
|
||||
departmentIds: [],
|
||||
});
|
||||
|
||||
const paramRef = computed(() => {
|
||||
return {
|
||||
limit: data.page.current,
|
||||
size: data.page.pageSize,
|
||||
departmentId: data.searchConfig.deptId,
|
||||
keyword: data.searchConfig.keyword,
|
||||
};
|
||||
});
|
||||
|
||||
watch(
|
||||
() => data.page.current,
|
||||
() => {
|
||||
getList();
|
||||
},
|
||||
);
|
||||
onMounted(() => {
|
||||
show();
|
||||
});
|
||||
async function show() {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.page.current = 1;
|
||||
data.page.total = 0;
|
||||
data.searchConfig.deptId = '';
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) {
|
||||
data.selectedIds = cloneDeep(props.selectedIds);
|
||||
}
|
||||
await getList();
|
||||
await getSelectedList();
|
||||
}
|
||||
function submit() {
|
||||
if (data.isDeptSelect) {
|
||||
emits('change', data.departmentIds, data.selectType);
|
||||
} else {
|
||||
emits('change', data.selectedIds, data.selectType);
|
||||
changeSelectedNames();
|
||||
}
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
data.list = [];
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
emits('close', false);
|
||||
}
|
||||
function checked(item) {
|
||||
if (
|
||||
props.disabledIds &&
|
||||
Array.isArray(props.disabledIds) &&
|
||||
props.disabledIds.includes(item.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (props.multiple && props.multiple == true) {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === item.id),
|
||||
1,
|
||||
);
|
||||
data.selectedList.splice(
|
||||
data.selectedList.findIndex((ele) => ele.id === item.id),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
data.selectedIds.push(item.id);
|
||||
data.selectedList.push(item);
|
||||
}
|
||||
} else {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
data.list = [];
|
||||
data.page.total = 0;
|
||||
let res = await getUserList(paramRef.value);
|
||||
if (res.total) {
|
||||
data.page.total = res.total;
|
||||
}
|
||||
if (res.list.length > 0) {
|
||||
res.list.forEach((ele) => {
|
||||
let item = {
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
};
|
||||
data.list.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
function search(keyword: string) {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.keyword = keyword;
|
||||
getList();
|
||||
}
|
||||
function handleSelect(deptId = '') {
|
||||
if (data.isDeptSelect) return;
|
||||
data.page.current = 1;
|
||||
data.searchConfig.deptId = deptId;
|
||||
getList();
|
||||
}
|
||||
|
||||
function handleCheck(ids = []) {
|
||||
data.departmentIds = ids;
|
||||
}
|
||||
async function getSelectedList() {
|
||||
let users = await getUserMulti(data.selectedIds.join(','));
|
||||
if (users.length > 0) {
|
||||
users.forEach((ele) => {
|
||||
data.selectedList.push({
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
});
|
||||
});
|
||||
changeSelectedNames();
|
||||
}
|
||||
}
|
||||
function changeSelectedNames() {
|
||||
let userNames = data.selectedList
|
||||
.map((ele) => {
|
||||
return ele.name;
|
||||
})
|
||||
.join(',');
|
||||
emits('changeNames', userNames);
|
||||
}
|
||||
function abolishChecked(id: string) {
|
||||
data.selectedList = data.selectedList.filter((ele) => {
|
||||
return ele.id != id;
|
||||
});
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === id),
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
function handleTypeChange(val) {
|
||||
data.isDeptSelect = !!val;
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.header-box {
|
||||
margin: 10px 20px 10px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
|
||||
.required-dot {
|
||||
color: #ff4d4f;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
236
src/components/SelectOrganizational/src/SelectMember.vue
Normal file
236
src/components/SelectOrganizational/src/SelectMember.vue
Normal file
@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<ModalPanel
|
||||
:visible="props.visible"
|
||||
:width="1200"
|
||||
:title="t('添加人员')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<template #left v-if="isShowTree">
|
||||
<OrganizationalTree @select="handleSelect" />
|
||||
</template>
|
||||
<!-- 已选 -->
|
||||
<Selected
|
||||
v-if="visible"
|
||||
type="user"
|
||||
:list="data.selectedList"
|
||||
@abolish="abolishChecked"
|
||||
:disabledIds="props.disabledIds"
|
||||
/>
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-page-box" v-if="visible && data.list.length > 0">
|
||||
<UserCard
|
||||
:class="data.selectedIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
:disabled="props.disabledIds && props.disabledIds.includes(item.id) ? true : false"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
:isShowTree="isShowTree"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="data.selectedIds.includes(item.id)" />
|
||||
</template>
|
||||
</UserCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="data.page.current"
|
||||
:pageSize="data.page.pageSize"
|
||||
:total="data.page.total"
|
||||
show-less-items
|
||||
/></div>
|
||||
</div>
|
||||
<EmptyBox v-if="data.list.length == 0" />
|
||||
</ModalPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, watch } from 'vue';
|
||||
import OrganizationalTree from '/@/components/SelectOrganizational/src/OrganizationalTree.vue';
|
||||
import UserCard from '/@/components/SelectOrganizational/src/card/UserCard.vue';
|
||||
import Selected from '/@/components/SelectOrganizational/src/Selected.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
import { getUserList, getUserMulti } from '/@/api/system/user';
|
||||
import { UserInfo } from '/@/api/system/user/model';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change', 'changeNames', 'close']);
|
||||
|
||||
const props = defineProps({
|
||||
selectedIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
disabledIds: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
multiple: Boolean,
|
||||
visible: Boolean,
|
||||
isShowTree: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
let data: {
|
||||
multiSelect: boolean;
|
||||
page: { current: number; total: number; pageSize: number };
|
||||
list: Array<UserInfo>;
|
||||
selectedList: Array<UserInfo>;
|
||||
selectedIds: Array<string>;
|
||||
searchConfig: {
|
||||
keyword: string;
|
||||
deptId: string;
|
||||
};
|
||||
} = reactive({
|
||||
multiSelect: false,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 9,
|
||||
},
|
||||
selectedIds: [],
|
||||
list: [],
|
||||
selectedList: [],
|
||||
searchConfig: {
|
||||
keyword: '',
|
||||
deptId: '',
|
||||
},
|
||||
});
|
||||
|
||||
const paramRef = computed(() => {
|
||||
return {
|
||||
limit: data.page.current,
|
||||
size: data.page.pageSize,
|
||||
departmentId: data.searchConfig.deptId,
|
||||
keyword: data.searchConfig.keyword,
|
||||
};
|
||||
});
|
||||
|
||||
watch(
|
||||
() => data.page.current,
|
||||
() => {
|
||||
getList();
|
||||
},
|
||||
);
|
||||
onMounted(() => {
|
||||
show();
|
||||
});
|
||||
async function show() {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.page.current = 1;
|
||||
data.page.total = 0;
|
||||
data.searchConfig.deptId = '';
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) {
|
||||
data.selectedIds = cloneDeep(props.selectedIds);
|
||||
}
|
||||
await getList();
|
||||
await getSelectedList();
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds);
|
||||
changeSelectedNames();
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
data.list = [];
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
emits('close', false);
|
||||
}
|
||||
function checked(item) {
|
||||
if (
|
||||
props.disabledIds &&
|
||||
Array.isArray(props.disabledIds) &&
|
||||
props.disabledIds.includes(item.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (props.multiple && props.multiple == true) {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === item.id),
|
||||
1,
|
||||
);
|
||||
data.selectedList.splice(
|
||||
data.selectedList.findIndex((ele) => ele.id === item.id),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
data.selectedIds.push(item.id);
|
||||
data.selectedList.push(item);
|
||||
}
|
||||
} else {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
data.list = [];
|
||||
data.page.total = 0;
|
||||
let res = await getUserList(paramRef.value);
|
||||
if (res.total) {
|
||||
data.page.total = res.total;
|
||||
}
|
||||
if (res.list.length > 0) {
|
||||
res.list.forEach((ele) => {
|
||||
let item = {
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
};
|
||||
data.list.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
function search(keyword: string) {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.keyword = keyword;
|
||||
getList();
|
||||
}
|
||||
function handleSelect(deptId = '') {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.deptId = deptId;
|
||||
getList();
|
||||
}
|
||||
async function getSelectedList() {
|
||||
let users = await getUserMulti(data.selectedIds.join(','));
|
||||
if (users.length > 0) {
|
||||
users.forEach((ele) => {
|
||||
data.selectedList.push({
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
});
|
||||
});
|
||||
changeSelectedNames();
|
||||
}
|
||||
}
|
||||
function changeSelectedNames() {
|
||||
let userNames = data.selectedList
|
||||
.map((ele) => {
|
||||
return ele.name;
|
||||
})
|
||||
.join(',');
|
||||
emits('changeNames', userNames);
|
||||
}
|
||||
function abolishChecked(id: string) {
|
||||
data.selectedList = data.selectedList.filter((ele) => {
|
||||
return ele.id != id;
|
||||
});
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === id),
|
||||
1,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
201
src/components/SelectOrganizational/src/SelectPost.vue
Normal file
201
src/components/SelectOrganizational/src/SelectPost.vue
Normal file
@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div @click="show"><slot></slot></div>
|
||||
|
||||
<ModalPanel
|
||||
:visible="data.visible"
|
||||
:width="1200"
|
||||
:title="t('添加岗位')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<template #left>
|
||||
<OrganizationalTree @select="handleSelect" />
|
||||
</template>
|
||||
<!-- 已选 -->
|
||||
<Selected type="post" :list="data.selectedList" @abolish="abolishChecked" />
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-page-box" v-if="data.list.length > 0">
|
||||
<PostCard
|
||||
:class="data.selectedIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="data.selectedIds.includes(item.id)" />
|
||||
</template>
|
||||
</PostCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="data.page.current"
|
||||
:pageSize="data.page.pageSize"
|
||||
:total="data.page.total"
|
||||
show-less-items
|
||||
@change="pageChange"
|
||||
/></div>
|
||||
</div>
|
||||
<EmptyBox v-if="data.list.length == 0" />
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue';
|
||||
import OrganizationalTree from './OrganizationalTree.vue';
|
||||
import PostCard from './card/PostCard.vue';
|
||||
import Selected from './Selected.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
|
||||
import { getPostTree, getPostMulti } from '/@/api/system/post';
|
||||
import { PostInfo } from '/@/api/system/post/model';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedIds: Array<string>;
|
||||
multiple?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedIds: () => {
|
||||
return [];
|
||||
},
|
||||
disabledIds: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
visible: boolean;
|
||||
multiSelect: boolean;
|
||||
page: { current: number; total: number; pageSize: number };
|
||||
list: Array<PostInfo>;
|
||||
selectedList: Array<PostInfo>;
|
||||
selectedIds: Array<string>;
|
||||
searchConfig: {
|
||||
keyword: string;
|
||||
deptId: string;
|
||||
};
|
||||
} = reactive({
|
||||
visible: false,
|
||||
multiSelect: false,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 9,
|
||||
},
|
||||
selectedIds: [],
|
||||
list: [],
|
||||
selectedList: [],
|
||||
searchConfig: {
|
||||
keyword: '',
|
||||
deptId: '',
|
||||
},
|
||||
});
|
||||
|
||||
async function show() {
|
||||
data.page.current = 1;
|
||||
data.page.total = 0;
|
||||
data.searchConfig.deptId = '';
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) data.selectedIds = props.selectedIds;
|
||||
await getList();
|
||||
await getSelectedList();
|
||||
data.visible = true;
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
data.list = [];
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.visible = false;
|
||||
}
|
||||
function checked(item) {
|
||||
if (props.multiple && props.multiple == true) {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === item.id),
|
||||
1,
|
||||
);
|
||||
data.selectedList.splice(
|
||||
data.selectedList.findIndex((ele) => ele.id === item.id),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
data.selectedIds.push(item.id);
|
||||
data.selectedList.push(item);
|
||||
}
|
||||
} else {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
data.list = [];
|
||||
data.page.total = 0;
|
||||
let params = {
|
||||
limit: data.page.current,
|
||||
size: data.page.pageSize,
|
||||
departmentId: data.searchConfig.deptId,
|
||||
keyword: data.searchConfig.keyword,
|
||||
};
|
||||
let res = await getPostTree(params);
|
||||
if (res.total) {
|
||||
data.page.total = res.total;
|
||||
}
|
||||
if (res.list.length > 0) {
|
||||
res.list.forEach((ele) => {
|
||||
let item = {
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
code: ele.code,
|
||||
};
|
||||
data.list.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
function search(keyword: string) {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.keyword = keyword;
|
||||
getList();
|
||||
}
|
||||
function handleSelect(deptId = '') {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.deptId = deptId;
|
||||
getList();
|
||||
}
|
||||
function pageChange(pagination) {
|
||||
data.page.current = pagination.current;
|
||||
}
|
||||
async function getSelectedList() {
|
||||
let list = await getPostMulti(data.selectedIds.join(','));
|
||||
if (list.length > 0) {
|
||||
list.forEach((ele) => {
|
||||
data.selectedList.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
code: ele.code,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
function abolishChecked(id: string) {
|
||||
data.selectedList = data.selectedList.filter((ele) => {
|
||||
return ele.id != id;
|
||||
});
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === id),
|
||||
1,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
165
src/components/SelectOrganizational/src/SelectRole.vue
Normal file
165
src/components/SelectOrganizational/src/SelectRole.vue
Normal file
@ -0,0 +1,165 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<div @click="show"><slot></slot></div>
|
||||
|
||||
<ModalPanel
|
||||
:visible="data.visible"
|
||||
:width="1200"
|
||||
:title="t('添加角色')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<!-- 已选 -->
|
||||
<Selected type="role" :list="data.selectedList" @abolish="abolishChecked" />
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-page-box" v-if="data.list.length > 0">
|
||||
<RoleCard
|
||||
:class="data.selectedIds && data.selectedIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox
|
||||
size="small"
|
||||
:checked="data.selectedIds && data.selectedIds.includes(item.id)"
|
||||
/>
|
||||
</template>
|
||||
</RoleCard>
|
||||
</div>
|
||||
<EmptyBox v-if="data.list.length == 0" />
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue';
|
||||
import RoleCard from './card/RoleCard.vue';
|
||||
import Selected from './Selected.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
|
||||
import { getRoleList, getRoleMulti } from '/@/api/system/role';
|
||||
import { RoleInfo } from '/@/api/system/role/model';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedIds: Array<string>;
|
||||
multiple?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedIds: () => {
|
||||
return [];
|
||||
},
|
||||
disabledIds: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
visible: boolean;
|
||||
multiSelect: boolean;
|
||||
list: Array<RoleInfo>;
|
||||
selectedList: Array<RoleInfo>;
|
||||
selectedIds: Array<string>;
|
||||
searchConfig: {
|
||||
keyword: string;
|
||||
};
|
||||
} = reactive({
|
||||
visible: false,
|
||||
multiSelect: false,
|
||||
selectedIds: [],
|
||||
list: [],
|
||||
selectedList: [],
|
||||
searchConfig: {
|
||||
keyword: '',
|
||||
},
|
||||
});
|
||||
|
||||
async function show() {
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) data.selectedIds = props.selectedIds;
|
||||
await getList();
|
||||
await getSelectedList();
|
||||
data.visible = true;
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds);
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
data.list = [];
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.visible = false;
|
||||
}
|
||||
function checked(item) {
|
||||
if (props.multiple && props.multiple == true) {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === item.id),
|
||||
1,
|
||||
);
|
||||
data.selectedList.splice(
|
||||
data.selectedList.findIndex((ele) => ele.id === item.id),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
data.selectedIds.push(item.id);
|
||||
data.selectedList.push(item);
|
||||
}
|
||||
} else {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
data.list = [];
|
||||
let params = {
|
||||
keyword: data.searchConfig.keyword,
|
||||
};
|
||||
let list = await getRoleList(params);
|
||||
if (list.length > 0) {
|
||||
list.forEach((ele) => {
|
||||
data.list.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
code: ele.code,
|
||||
count: ele.count, //角色人数
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
function search(keyword: string) {
|
||||
data.searchConfig.keyword = keyword;
|
||||
getList();
|
||||
}
|
||||
async function getSelectedList() {
|
||||
let list = await getRoleMulti(data.selectedIds.join(','));
|
||||
if (list.length > 0) {
|
||||
list.forEach((ele) => {
|
||||
data.selectedList.push({
|
||||
name: ele.name,
|
||||
id: ele.id,
|
||||
code: ele.code,
|
||||
count: ele.count, //角色人数
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
function abolishChecked(id: string) {
|
||||
data.selectedList = data.selectedList.filter((ele) => {
|
||||
return ele.id != id;
|
||||
});
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === id),
|
||||
1,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
269
src/components/SelectOrganizational/src/SelectUser.vue
Normal file
269
src/components/SelectOrganizational/src/SelectUser.vue
Normal file
@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<div style="width: 100%" @click="show">
|
||||
<slot></slot>
|
||||
|
||||
<ModalPanel
|
||||
:visible="data.visible"
|
||||
:width="1200"
|
||||
:title="t('添加人员')"
|
||||
@submit="submit"
|
||||
@close="close"
|
||||
>
|
||||
<template #left>
|
||||
<OrganizationalTree @select="handleSelect" />
|
||||
</template>
|
||||
<!-- 已选 -->
|
||||
<Selected
|
||||
v-if="data.visible"
|
||||
type="user"
|
||||
:list="data.selectedList"
|
||||
@abolish="abolishChecked"
|
||||
:disabledIds="props.disabledIds"
|
||||
/>
|
||||
<SearchBox @search="search" />
|
||||
<div class="list-page-box" v-if="data.visible && data.list.length > 0">
|
||||
<UserCard
|
||||
:class="data.selectedIds.includes(item.id) ? 'picked' : 'not-picked'"
|
||||
:disabled="props.disabledIds && props.disabledIds.includes(item.id) ? true : false"
|
||||
v-for="(item, index) in data.list"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="checked(item)"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="data.selectedIds.includes(item.id)" />
|
||||
</template>
|
||||
</UserCard>
|
||||
<div class="page-box">
|
||||
<a-pagination
|
||||
v-model:current="data.page.current"
|
||||
:pageSize="data.page.pageSize"
|
||||
:total="data.page.total"
|
||||
show-less-items
|
||||
/></div>
|
||||
</div>
|
||||
<EmptyBox v-if="data.list.length == 0" />
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, watch } from 'vue';
|
||||
import OrganizationalTree from './OrganizationalTree.vue';
|
||||
import UserCard from './card/UserCard.vue';
|
||||
import Selected from './Selected.vue';
|
||||
import { ModalPanel, EmptyBox, SearchBox } from '/@/components/ModalPanel/index';
|
||||
import { getUserList, getUserMulti } from '/@/api/system/user';
|
||||
import { UserInfo } from '/@/api/system/user/model';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change', 'changeNames', 'close']);
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedIds: Array<string> | string;
|
||||
disabledIds?: Array<string>;
|
||||
multiple?: Boolean;
|
||||
visible?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedIds: () => {
|
||||
return [];
|
||||
},
|
||||
disabledIds: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
visible: boolean;
|
||||
multiSelect: boolean;
|
||||
page: { current: number; total: number; pageSize: number };
|
||||
list: Array<UserInfo>;
|
||||
selectedList: Array<UserInfo>;
|
||||
selectedIds: Array<string>;
|
||||
searchConfig: {
|
||||
keyword: string;
|
||||
deptId: string;
|
||||
};
|
||||
} = reactive({
|
||||
visible: false,
|
||||
multiSelect: false,
|
||||
page: {
|
||||
current: 1,
|
||||
total: 0,
|
||||
pageSize: 9,
|
||||
},
|
||||
selectedIds: [],
|
||||
list: [],
|
||||
selectedList: [],
|
||||
searchConfig: {
|
||||
keyword: '',
|
||||
deptId: '',
|
||||
},
|
||||
});
|
||||
|
||||
const paramRef = computed(() => {
|
||||
return {
|
||||
limit: data.page.current,
|
||||
size: data.page.pageSize,
|
||||
departmentId: data.searchConfig.deptId,
|
||||
keyword: data.searchConfig.keyword,
|
||||
};
|
||||
});
|
||||
|
||||
watch(
|
||||
() => data.page.current,
|
||||
() => {
|
||||
getList();
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) {
|
||||
data.selectedIds = cloneDeep(props.selectedIds);
|
||||
getSelectedList();
|
||||
}
|
||||
});
|
||||
async function show() {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.page.current = 1;
|
||||
data.page.total = 0;
|
||||
data.searchConfig.deptId = '';
|
||||
if (props.selectedIds && Array.isArray(props.selectedIds)) {
|
||||
data.selectedIds = cloneDeep(props.selectedIds);
|
||||
}
|
||||
if ((props.visible !== undefined && props.visible) || props.visible === undefined) {
|
||||
await getList();
|
||||
await getSelectedList();
|
||||
data.visible = true;
|
||||
} else {
|
||||
data.visible = false;
|
||||
}
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds, data.selectedList);
|
||||
changeSelectedNames();
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
data.list = [];
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
data.visible = false;
|
||||
emits('close');
|
||||
}
|
||||
function checked(item) {
|
||||
if (
|
||||
props.disabledIds &&
|
||||
Array.isArray(props.disabledIds) &&
|
||||
props.disabledIds.includes(item.id)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (props.multiple && props.multiple == true) {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === item.id),
|
||||
1,
|
||||
);
|
||||
data.selectedList.splice(
|
||||
data.selectedList.findIndex((ele) => ele.id === item.id),
|
||||
1,
|
||||
);
|
||||
} else {
|
||||
data.selectedIds.push(item.id);
|
||||
data.selectedList.push(item);
|
||||
}
|
||||
} else {
|
||||
if (data.selectedIds.includes(item.id)) {
|
||||
data.selectedIds = [];
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
data.selectedList = [item];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getList() {
|
||||
data.list = [];
|
||||
data.page.total = 0;
|
||||
// let params = {
|
||||
// limit: data.page.current,
|
||||
// size: data.page.pageSize,
|
||||
// departmentId: data.searchConfig.deptId,
|
||||
// keyword: data.searchConfig.keyword,
|
||||
// };
|
||||
|
||||
console.log('params', paramRef);
|
||||
let res = await getUserList(paramRef.value);
|
||||
if (res.total) {
|
||||
data.page.total = res.total;
|
||||
}
|
||||
if (res.list.length > 0) {
|
||||
res.list.forEach((ele) => {
|
||||
let item = {
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
mobile: ele.mobile, //联系电话
|
||||
};
|
||||
data.list.push(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
function search(keyword: string) {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.keyword = keyword;
|
||||
console.log('search');
|
||||
getList();
|
||||
}
|
||||
function handleSelect(deptId = '') {
|
||||
data.page.current = 1;
|
||||
data.searchConfig.deptId = deptId;
|
||||
console.log('handleSelect');
|
||||
getList();
|
||||
}
|
||||
|
||||
// async function pageChange(pagination) {
|
||||
// data.page.current = pagination.current;
|
||||
// console.log('data.page', data.page, pagination);
|
||||
// await getList();
|
||||
// }
|
||||
|
||||
async function getSelectedList() {
|
||||
let users = await getUserMulti(data.selectedIds.join(','));
|
||||
if (users.length > 0) {
|
||||
users.forEach((ele) => {
|
||||
data.selectedList.push({
|
||||
name: ele.name, //姓名
|
||||
id: ele.id, //ID
|
||||
code: ele.code, //code
|
||||
gender: !isNaN(ele.gender) ? ele.gender : -1, //性别
|
||||
mobile: ele.mobile, //联系电话
|
||||
});
|
||||
});
|
||||
changeSelectedNames();
|
||||
}
|
||||
}
|
||||
function changeSelectedNames() {
|
||||
let userNames = data.selectedList
|
||||
.map((ele) => {
|
||||
return ele.name;
|
||||
})
|
||||
.join(',');
|
||||
emits('changeNames', userNames);
|
||||
}
|
||||
function abolishChecked(id: string) {
|
||||
data.selectedList = data.selectedList.filter((ele) => {
|
||||
return ele.id != id;
|
||||
});
|
||||
data.selectedIds.splice(
|
||||
data.selectedIds.findIndex((itemId) => itemId === id),
|
||||
1,
|
||||
);
|
||||
}
|
||||
</script>
|
||||
163
src/components/SelectOrganizational/src/Selected.vue
Normal file
163
src/components/SelectOrganizational/src/Selected.vue
Normal file
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div ref="drawer">
|
||||
<div class="flex justify-between mr-5">
|
||||
<NodeHead :node-name="listTitle" />
|
||||
<a-button @click="show">{{ title }}</a-button></div
|
||||
>
|
||||
<a-drawer
|
||||
:getContainer="() => $refs.drawer"
|
||||
placement="right"
|
||||
:visible="data.visible"
|
||||
:closable="false"
|
||||
:mask="false"
|
||||
>
|
||||
<div class="selected-head title">
|
||||
<NodeHead :node-name="title" />
|
||||
<div class="close-icon" @click="close">+</div>
|
||||
</div>
|
||||
<div class="list-box" v-if="props.list && props.list.length > 0">
|
||||
<component
|
||||
:is="componentName"
|
||||
v-for="(item, index) in props.list"
|
||||
class="picked"
|
||||
:key="index"
|
||||
:item="item"
|
||||
@click="abolish(item.id)"
|
||||
:disabled="props.disabledIds && props.disabledIds.includes(item.id) ? true : false"
|
||||
>
|
||||
<template #check>
|
||||
<a-checkbox size="small" :checked="true" />
|
||||
</template>
|
||||
</component>
|
||||
</div>
|
||||
<EmptyBox v-else />
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive } from 'vue';
|
||||
import UserCard from './card/UserCard.vue';
|
||||
import RoleCard from './card/RoleCard.vue';
|
||||
import PostCard from './card/PostCard.vue';
|
||||
import { EmptyBox, NodeHead } from '/@/components/ModalPanel/index';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['abolish']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
type: String;
|
||||
list: Array<{ id: string }>;
|
||||
disabledIds?: Array<string>;
|
||||
}>(),
|
||||
{
|
||||
type: () => {
|
||||
return '';
|
||||
},
|
||||
list: () => {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
let data = reactive({
|
||||
visible: false,
|
||||
});
|
||||
const componentName = computed(() => {
|
||||
if (props.type == 'user') {
|
||||
return UserCard;
|
||||
} else if (props.type == 'role') {
|
||||
return RoleCard;
|
||||
} else if (props.type == 'post') {
|
||||
return PostCard;
|
||||
} else {
|
||||
return UserCard;
|
||||
}
|
||||
});
|
||||
const title = computed(() => {
|
||||
if (props.type == 'user') {
|
||||
return t('已选用户');
|
||||
} else if (props.type == 'role') {
|
||||
return t('已选角色');
|
||||
} else if (props.type == 'post') {
|
||||
return t('已选岗位');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
const listTitle = computed(() => {
|
||||
if (props.type == 'user') {
|
||||
return t('用户列表');
|
||||
} else if (props.type == 'role') {
|
||||
return t('角色列表');
|
||||
} else if (props.type == 'post') {
|
||||
return t('岗位列表');
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
async function show() {
|
||||
data.visible = true;
|
||||
}
|
||||
function close() {
|
||||
data.visible = false;
|
||||
}
|
||||
function abolish(id: string) {
|
||||
emits('abolish', id);
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-content-wrapper) {
|
||||
width: 100% !important;
|
||||
box-shadow: 0 2px 2px 2px rgb(0 0 0 / 4%);
|
||||
}
|
||||
|
||||
:deep(.ant-drawer-open) {
|
||||
position: absolute;
|
||||
width: calc(100% - 244px) !important;
|
||||
top: 50px;
|
||||
left: 240px;
|
||||
box-shadow: -5px 5px 4px 1px rgb(0 0 0 / 6%);
|
||||
height: calc(100% - 110px);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.list-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.selected-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.selected-btn {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.close-icon {
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
123
src/components/SelectOrganizational/src/card/PostCard.vue
Normal file
123
src/components/SelectOrganizational/src/card/PostCard.vue
Normal file
@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<div class="list-item">
|
||||
<div class="item-box">
|
||||
<div class="item-left"><img :src="PostImg" /></div>
|
||||
<div class="item-right">
|
||||
<div class="item-title">{{ t('岗位名称') }}</div>
|
||||
<div class="item-form-name">{{ props.item?.name }}</div>
|
||||
</div>
|
||||
<div class="fixed-checked">
|
||||
<slot name="check"></slot>
|
||||
</div>
|
||||
<div class="fixed-icon">
|
||||
<IconFontSymbol icon="post" fill-color="#fdf5ef" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import PostImg from '/@/assets/workflow/post.png';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let props = defineProps({
|
||||
item: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@custom-color: #ff9935;
|
||||
@bg-color: #fffbf7;
|
||||
|
||||
.list-item {
|
||||
width: 30%;
|
||||
height: 100px;
|
||||
background: @bg-color;
|
||||
border-color: transparent;
|
||||
border-radius: 8px;
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border: 1px dotted @custom-color;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
margin: 14px;
|
||||
position: relative;
|
||||
|
||||
.item-left {
|
||||
width: 30%;
|
||||
margin-right: 14px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-right {
|
||||
.item-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
margin: 10px 0 4px 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.item-form-name {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-checked {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.fixed-icon {
|
||||
position: absolute;
|
||||
right: -24px;
|
||||
font-size: 70px;
|
||||
transform: rotate(48deg);
|
||||
top: -24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: @custom-color;
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked::after),
|
||||
:deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-inner),
|
||||
:deep(.ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
.not-picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
131
src/components/SelectOrganizational/src/card/RoleCard.vue
Normal file
131
src/components/SelectOrganizational/src/card/RoleCard.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<div class="list-item">
|
||||
<div class="item-box">
|
||||
<div class="item-left"><img :src="RoleImg" /></div>
|
||||
<div class="item-right item-role-box">
|
||||
<div class="item-title">{{ t('角色名称') }}</div>
|
||||
<div class="item-form-name">{{ props.item?.name }}</div>
|
||||
</div>
|
||||
<div class="item-right">
|
||||
<div class="item-title">{{ t('角色人数') }}</div>
|
||||
<div class="item-form-name"> {{ props.item?.count ? props.item.count : 0 }}人</div>
|
||||
</div>
|
||||
<div class="fixed-checked">
|
||||
<slot name="check"></slot>
|
||||
</div>
|
||||
<div class="fixed-icon">
|
||||
<IconFontSymbol icon="role" fill-color="#e0f8f2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import RoleImg from '/@/assets/workflow/role.png';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let props = defineProps({
|
||||
item: Object,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@custom-color: #0cc4a6;
|
||||
@bg-color: #effbf9;
|
||||
|
||||
.list-item {
|
||||
width: 30%;
|
||||
height: 120px;
|
||||
background: @bg-color;
|
||||
border-color: transparent;
|
||||
border-radius: 8px;
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border: 1px dotted @custom-color;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
margin: 14px;
|
||||
position: relative;
|
||||
|
||||
.item-left {
|
||||
width: 30%;
|
||||
margin-right: 14px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-right {
|
||||
.item-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #999999;
|
||||
margin: 10px 0 4px 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.item-form-name {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-checked {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.fixed-icon {
|
||||
position: absolute;
|
||||
right: -24px;
|
||||
font-size: 70px;
|
||||
transform: rotate(48deg);
|
||||
top: -24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-role-box {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: @custom-color;
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked::after),
|
||||
:deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-inner),
|
||||
:deep(.ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
border-color: @custom-color;
|
||||
}
|
||||
|
||||
.not-picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
193
src/components/SelectOrganizational/src/card/UserCard.vue
Normal file
193
src/components/SelectOrganizational/src/card/UserCard.vue
Normal file
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div class="list-item">
|
||||
<div class="item-box">
|
||||
<div class="item-left"><img :src="genderImg" /></div>
|
||||
<div class="z-10">
|
||||
<div class="item-right flex items-center">
|
||||
<div class="item-title">{{ t('编码') }}</div>
|
||||
<a-tooltip v-if="item?.code && item.code.length > 12" :title="item.code">
|
||||
<div class="item-form-name"> {{ `${item.code.slice(0, 12)}...` }}</div>
|
||||
</a-tooltip>
|
||||
<div class="item-form-name" v-else> {{ item?.code || '-' }}</div>
|
||||
</div>
|
||||
<div class="item-right flex items-center">
|
||||
<div class="item-title">{{ t('姓名') }}</div>
|
||||
<a-tooltip v-if="item?.name && item.name.length > 8" :title="item.name">
|
||||
<div class="item-form-name"> {{ `${item.name.slice(0, 8)}...` }}</div>
|
||||
</a-tooltip>
|
||||
<div class="item-form-name" v-else> {{ item?.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div v-if="props.disabled">
|
||||
<div class="fixed-checked"> 禁用 </div>
|
||||
</div> -->
|
||||
<div class="fixed-checked" v-if="hasCheckSlot">
|
||||
<slot name="check"></slot>
|
||||
</div>
|
||||
<div class="fixed-icon">
|
||||
<IconFontSymbol icon="user" :fillColor="genderFillColor" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import defaultImg from '/@/assets/workflow/default.png';
|
||||
import FemaleImg from '/@/assets/workflow/female.png';
|
||||
import MaleImg from '/@/assets/workflow/male.png';
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
import { GenderEnum } from '/@/enums/userEnum';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
let props = defineProps({
|
||||
item: Object,
|
||||
disabled: Boolean,
|
||||
isShowTree: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const hasCheckSlot = computed(() => {
|
||||
return !!useSlots().check;
|
||||
});
|
||||
const genderImg = computed(() => {
|
||||
switch (props.item?.gender) {
|
||||
case GenderEnum.FEMALE:
|
||||
return FemaleImg;
|
||||
case GenderEnum.MALE:
|
||||
return MaleImg;
|
||||
default:
|
||||
return defaultImg;
|
||||
}
|
||||
});
|
||||
const genderFillColor = computed(() => {
|
||||
switch (props.item?.gender) {
|
||||
case GenderEnum.FEMALE:
|
||||
return '#ffedf5';
|
||||
case GenderEnum.MALE:
|
||||
return '#e9f0fe';
|
||||
default:
|
||||
return '#f1ecfe';
|
||||
}
|
||||
});
|
||||
|
||||
let fontcolor = computed(() => {
|
||||
switch (props.item?.gender) {
|
||||
case GenderEnum.FEMALE:
|
||||
return '#ffd1d7';
|
||||
case GenderEnum.MALE:
|
||||
return '#3c7eff';
|
||||
default:
|
||||
return '#b389ff';
|
||||
}
|
||||
});
|
||||
let bgcolor = computed(() => {
|
||||
switch (props.item?.gender) {
|
||||
case GenderEnum.FEMALE:
|
||||
return '#fef6fa';
|
||||
case GenderEnum.MALE:
|
||||
return '#f3f8ff';
|
||||
default:
|
||||
return '#f5f1fd';
|
||||
}
|
||||
});
|
||||
|
||||
const itemleftwidth = computed(() => {
|
||||
return props.isShowTree ? '30%' : '25%';
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.list-item {
|
||||
width: 30%;
|
||||
background: v-bind(bgcolor);
|
||||
border-color: transparent;
|
||||
border-radius: 8px;
|
||||
margin-left: 20px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border: 1px dotted v-bind(fontcolor);
|
||||
}
|
||||
|
||||
.item-box {
|
||||
display: flex;
|
||||
margin: 14px;
|
||||
position: relative;
|
||||
|
||||
.item-left {
|
||||
width: v-bind(itemleftwidth);
|
||||
margin-right: 14px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-right {
|
||||
.item-title {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #999;
|
||||
margin: 10px 10px 4px 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.item-form-name {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 8px 0 4px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-checked {
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
z-index: 1;
|
||||
right: -6px;
|
||||
}
|
||||
|
||||
.fixed-icon {
|
||||
position: absolute;
|
||||
right: -24px;
|
||||
font-size: 70px;
|
||||
transform: rotate(48deg);
|
||||
top: -24px;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-color: v-bind(fontcolor);
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
background-color: v-bind(fontcolor);
|
||||
border-color: v-bind(fontcolor);
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked::after),
|
||||
:deep(.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-inner),
|
||||
:deep(.ant-checkbox:hover),
|
||||
:deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
|
||||
border-color: v-bind(fontcolor);
|
||||
}
|
||||
|
||||
.picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
border-color: v-bind(fontcolor);
|
||||
}
|
||||
|
||||
.not-picked {
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user