509 lines
12 KiB
Vue
509 lines
12 KiB
Vue
<template>
|
|
<uni-popup ref="orgPickerPopup" class="uni-popup" style="touch-action:none" :catchtouchmove="true"
|
|
@close="isShowPicker.value = false">
|
|
<uni-nav-bar v-if="showNav" statusBar :title="title" color="#fff" backgroundColor="#4C87F3"></uni-nav-bar>
|
|
<view class="w-orgPicker-popup" :style="{height: wheight + 'px'}">
|
|
<view style="padding: 0 0 16rpx 0; background-color: white;">
|
|
<view class="search">
|
|
<uni-search-bar v-model="searchValue" bgColor="#EEEEEE" radius="5" placeholder="搜索"
|
|
clearButton="auto" cancelButton="none" />
|
|
<!-- :readonly="type === 'dept' || type === 'role'" -->
|
|
</view>
|
|
<view style="padding: 6rpx 16rpx;">
|
|
<scroll-view scroll-x>
|
|
<uni-breadcrumb separator=">">
|
|
<uni-breadcrumb-item v-for="(org, index) in orgPaths" :key="index"
|
|
@tap="toDept(org.id, index)">
|
|
<view
|
|
:style="{color: index + 1 === orgPaths.length ? '#989996':'#1E90FD','font-size': '32rpx'}">
|
|
{{org.name}}
|
|
</view>
|
|
</uni-breadcrumb-item>
|
|
</uni-breadcrumb>
|
|
</scroll-view>
|
|
</view>
|
|
</view>
|
|
<scroll-view v-if="orgList.length > 0" class="w-org-list" scroll-y :style="{height: contentHeight + 'px'}">
|
|
<uni-list>
|
|
<uni-list-item clickable v-for="(org, i) in orgDatas" class="w-org-item"
|
|
:key="`${org.type}_${org.id}`" :showArrow="props.type === 'user' && org.type !== 'user'"
|
|
@click="selectChange(org)">
|
|
<template v-slot:header>
|
|
<view style="display: flex; align-items: center;">
|
|
<radio v-if="!(props.type === 'user' && org.type === 'dept')" :value="org.id"
|
|
:checked="org.selected" @click.stop="selectChange(org)"
|
|
style="transform:scale(0.9)" />
|
|
<view class="w-org-avatar">
|
|
<avatar v-if="org.type === 'user'" :name="org.name" :src="org.avatar"
|
|
:showName="false">
|
|
</avatar>
|
|
<image class="w-dept-img" lazy-load mode="aspectFit" v-else
|
|
src="/static/image/dept.png"></image>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<template v-slot:body>
|
|
<view style="flex: 1; display: flex; align-items: center;">
|
|
<text style="display: flex; align-items: center; font-size: 32rpx">{{org.name}}</text>
|
|
<view v-if="org.isLeader"
|
|
style="display: flex; align-items: center; margin-left: 16rpx;">
|
|
<uni-tag style="font-weight: 400;" type="warning" size="mini" text="部门负责人"
|
|
inverted></uni-tag>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<template v-slot:footer v-if="props.type !== 'user' && org.type !== 'user'">
|
|
<view @click.stop="toInnerOrg(org)" :class="{'w-org-next': true, 'w-org-dis': false}">
|
|
<view>下级</view>
|
|
<uni-icons type="redo" :size="20" color="#4478F7"></uni-icons>
|
|
</view>
|
|
</template>
|
|
</uni-list-item>
|
|
</uni-list>
|
|
</scroll-view>
|
|
<view v-else :style="{width: '100%', height: contentHeight + 'px'}">
|
|
<image mode="aspectFit" style="width: 100%;" src="/static/image/noUser.png"></image>
|
|
</view>
|
|
<view class="w-orgPicker-options">
|
|
<label v-show="props.multiple">
|
|
<radio :checked="false" style="transform: scale(0.8);" />
|
|
全选
|
|
</label>
|
|
<view @click="showSelected">
|
|
<text>已选 [{{selectedMap.size}}] 项 </text>
|
|
<uni-icons type="down" color="#4478F7"></uni-icons>
|
|
</view>
|
|
<view>
|
|
<button class="w-button" style="margin-right: 10px;" type="default" size="mini"
|
|
@click="close">取消</button>
|
|
<button class="w-button" type="primary" size="mini" @click="selectOk">确认</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</uni-popup>
|
|
<uni-popup ref="orgPickerSelectedPopup" type="bottom">
|
|
<view class="w-orgPicker-selected">
|
|
<view class="w-selected" v-for="org in selected" :key="`${org.type}_${org.id}`">
|
|
<view>
|
|
<avatar :type="org.type" v-if="org.type==='user'" :closeable="org.enableEdit" :src="org.avatar"
|
|
:size="25" :name="org.name" :showName="false"></avatar>
|
|
</view>
|
|
<view>{{org.name}}</view>
|
|
<button size="mini" @click="removeSelected(org)">移除</button>
|
|
</view>
|
|
</view>
|
|
</uni-popup>
|
|
</template>
|
|
|
|
<script setup>
|
|
import {
|
|
onMounted,
|
|
computed,
|
|
ref,
|
|
watch,
|
|
nextTick
|
|
} from "vue";
|
|
import {
|
|
onBackPress
|
|
} from "@dcloudio/uni-app";
|
|
import Avatar from '@/components/Avatar.vue'
|
|
import {
|
|
getOrgTree,
|
|
getUserByName,
|
|
getDeptByName,
|
|
getEnterpriseByName
|
|
} from '@/api/org.js'
|
|
|
|
const props = defineProps({
|
|
title: {
|
|
default: '请选择',
|
|
type: String
|
|
},
|
|
type: {
|
|
default: 'org', //org选择部门/人员 user-选人 dept-选部门 role-选角色
|
|
type: String
|
|
},
|
|
multiple: { //是否多选
|
|
default: false,
|
|
type: Boolean
|
|
},
|
|
selected: {
|
|
default: () => {
|
|
return []
|
|
},
|
|
type: Array
|
|
},
|
|
position: {
|
|
type: String,
|
|
default: 'right'
|
|
},
|
|
showNav: Boolean
|
|
})
|
|
|
|
const orgPickerPopup = ref()
|
|
const orgPickerSelectedPopup = ref()
|
|
const wheight = uni.getSystemInfoSync().windowHeight
|
|
|
|
const selectAll = ref(false)
|
|
const isShowPicker = ref(false)
|
|
|
|
//已选中的用户/角色/部门
|
|
const selectedMap = ref(null)
|
|
selectedMap.value = new Map()
|
|
|
|
//当前展示的部门id
|
|
const currentDeptId = ref(0)
|
|
const searchValue = ref('')
|
|
//搜索结果
|
|
const searchUsers = ref([])
|
|
//主数据
|
|
const orgList = ref([])
|
|
//部门路径
|
|
const orgPaths = ref([{
|
|
name: '组织',
|
|
id: 0
|
|
}])
|
|
|
|
const contentHeight = computed(() => {
|
|
const baseH = uni.getSystemInfoSync().windowHeight - 168
|
|
//减去导航栏高度
|
|
// #ifdef H5
|
|
return baseH - (props.showNav ? 44 : 0)
|
|
// #endif
|
|
// #ifndef H5
|
|
return baseH - (props.showNav ? 88 : 0)
|
|
// #endif
|
|
})
|
|
|
|
const selected = computed(() => {
|
|
const temp = []
|
|
selectedMap.value.forEach(v => temp.push({
|
|
id: v.id,
|
|
name: v.name,
|
|
avatar: v.avatar,
|
|
type: v.type
|
|
}))
|
|
return temp
|
|
})
|
|
|
|
const selectCount = computed(() => {
|
|
return selectedMap.value.size
|
|
})
|
|
|
|
const isSearchMode = ref(false)
|
|
const orgDatas = computed(() => {
|
|
if (searchValue.value.trim() !== '') {
|
|
return searchUsers.value
|
|
}
|
|
return orgList.value
|
|
})
|
|
|
|
const emit = defineEmits(['ok'])
|
|
|
|
//监听返回按钮
|
|
onBackPress(() => backToParent())
|
|
|
|
onMounted(() => {
|
|
if (props.type == "dept") {
|
|
orgPaths.value = [{
|
|
name: '部门',
|
|
id: 0
|
|
}]
|
|
}
|
|
})
|
|
|
|
function toDept(orgId, i) {
|
|
currentDeptId.value = orgId
|
|
orgPaths.value.length = i + 1
|
|
getOrgList()
|
|
}
|
|
|
|
function showPicker() {
|
|
//加载选中数据
|
|
(props.selected || []).forEach(org => {
|
|
const _org = Object.assign({}, org)
|
|
_org.selected = true
|
|
selectedMap.value.set(org.id + org.type, _org)
|
|
})
|
|
isShowPicker.value = true
|
|
}
|
|
|
|
function backToParent() {
|
|
if (orgPaths.value.length > 1) {
|
|
orgPaths.value.length--
|
|
currentDeptId.value = orgPaths.value[orgPaths.value.length - 1].id
|
|
getOrgList()
|
|
return true;
|
|
} else if (orgPickerPopup.value.close instanceof Function) {
|
|
orgPickerPopup.value.close()
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function toInnerOrg(org) {
|
|
console.log(org);
|
|
if (org.type === 'dept') {
|
|
currentDeptId.value = org.id
|
|
orgPaths.value.push({
|
|
name: org.name,
|
|
id: org.id
|
|
})
|
|
getOrgList()
|
|
} else if (org.type === 'enterprise') {
|
|
currentDeptId.value = org.id
|
|
orgPaths.value.push({
|
|
name: org.name,
|
|
id: org.id
|
|
})
|
|
getOrgList()
|
|
}
|
|
}
|
|
|
|
function getOrgList() {
|
|
const loginUser = uni.getStorageSync('loginUser')
|
|
getOrgTree({
|
|
deptId: currentDeptId.value + "P" + loginUser.sn,
|
|
type: props.type
|
|
}).then(rsp => {
|
|
orgList.value = rsp.data
|
|
loadSelected(orgList.value)
|
|
}).catch(err => {
|
|
|
|
})
|
|
}
|
|
|
|
function searchUser() {
|
|
searchUsers.value.length = 0
|
|
console.log(props.type, "type");
|
|
let request = null;
|
|
let name = ""
|
|
switch (props.type) {
|
|
case "user":
|
|
name = "userName"
|
|
request = getUserByName
|
|
break;
|
|
case "enterprise":
|
|
name = "enterpriseName";
|
|
request = getEnterpriseByName
|
|
break;
|
|
case "dept":
|
|
name = "deptName";
|
|
request = getDeptByName
|
|
break;
|
|
}
|
|
request({
|
|
[name]: searchValue.value.trim()
|
|
}).then(rsp => {
|
|
searchUsers.value = rsp.data.map(org => {
|
|
org.selected = selectedMap.value.has(org.id + org.type)
|
|
return org
|
|
})
|
|
}).catch(err => {
|
|
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 打开组织架构选择器
|
|
* @param {Object} rootDeptId 根部门ID
|
|
*/
|
|
function show(rootDeptId = 0) {
|
|
currentDeptId.value = rootDeptId
|
|
orgPaths.value[0].id = rootDeptId
|
|
getOrgList()
|
|
orgPickerPopup.value.open(props.position)
|
|
showPicker()
|
|
}
|
|
|
|
function close() {
|
|
selectedMap.value.clear()
|
|
orgPickerPopup.value.close()
|
|
}
|
|
|
|
/**
|
|
* 确认选中
|
|
*/
|
|
function selectOk() {
|
|
if (selected.value.length === 0) {
|
|
uni.showToast({
|
|
icon: 'none',
|
|
title: '无选中项😥'
|
|
})
|
|
} else {
|
|
emit('ok', selected.value)
|
|
close()
|
|
}
|
|
}
|
|
|
|
function selectChange(org) {
|
|
console.log(org, "org");
|
|
if (org.type === 'dept' && props.type === 'user') {
|
|
//选用户时不触发部门,直接进入子部门
|
|
toInnerOrg(org)
|
|
return;
|
|
}
|
|
const orgId = org.id + org.type
|
|
if (selectedMap.value.has(orgId)) {
|
|
//取消选中
|
|
org.selected = false
|
|
selectedMap.value.delete(orgId)
|
|
} else {
|
|
//选中
|
|
if (!props.multiple) {
|
|
//单选的话清除之前选的
|
|
selectedMap.value.forEach(v => v.selected = false)
|
|
// 清空之前选中的
|
|
orgDatas.value.forEach(v => v.selected = false)
|
|
org.selected = true
|
|
selectedMap.value.clear()
|
|
}
|
|
selectedMap.value.set(orgId, org)
|
|
console.log(selectedMap.value, "selectedMap.value", props);
|
|
}
|
|
}
|
|
|
|
//加载选中数据状态
|
|
function loadSelected(orgs) {
|
|
orgs.forEach(org => {
|
|
const orgId = org.id + org.type
|
|
org.selected = selectedMap.value.has(org.id + org.type)
|
|
})
|
|
}
|
|
|
|
function showSelected() {
|
|
if (selectedMap.value.size === 0) {
|
|
uni.showToast({
|
|
icon: 'none',
|
|
title: '没有选中的项'
|
|
})
|
|
} else {
|
|
orgPickerSelectedPopup.value.open()
|
|
}
|
|
}
|
|
|
|
function removeSelected(org) {
|
|
const orgId = org.id + org.type
|
|
selectedMap.value.get(orgId).selected = false
|
|
selectedMap.value.delete(orgId)
|
|
if (selectedMap.value.size === 0) {
|
|
orgPickerSelectedPopup.value.close()
|
|
}
|
|
}
|
|
|
|
watch(searchValue, () => {
|
|
if (searchValue.value.trim() !== '') {
|
|
searchUser()
|
|
}
|
|
})
|
|
|
|
defineExpose({
|
|
show,
|
|
close
|
|
})
|
|
</script>
|
|
|
|
<style lang="less" scoped>
|
|
page {
|
|
background-color: #F4F5F7;
|
|
}
|
|
|
|
.w-org-paths {
|
|
background-color: white;
|
|
}
|
|
|
|
:deep(.w-org-list) {
|
|
font-size: 32rpx;
|
|
margin-top: 26rpx;
|
|
|
|
.w-org-item {
|
|
.uni-list-item__container {
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
.w-org-avatar {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
|
|
.w-dept-img {
|
|
width: 51rpx;
|
|
height: 51rpx;
|
|
padding: 13rpx;
|
|
border-radius: 10px;
|
|
background-color: #c4d7ff;
|
|
}
|
|
|
|
margin-right: 26rpx;
|
|
}
|
|
|
|
.search {
|
|
.search-input {
|
|
font-size: 32rpx;
|
|
background-color: #F4F5F7;
|
|
border-radius: 13rpx;
|
|
}
|
|
}
|
|
|
|
.w-orgPicker-popup {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
background-color: #F4F5F7;
|
|
}
|
|
|
|
.w-org-next {
|
|
color: #4478F7;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.w-orgPicker-options {
|
|
height: 64rpx;
|
|
padding: 32rpx;
|
|
background-color: #F1F1F1;
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
&>label {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
&>view:nth-child(2) {
|
|
flex: 1;
|
|
color: #4478F7;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
.w-orgPicker-selected {
|
|
padding: 32rpx;
|
|
background-color: white;
|
|
|
|
.w-selected {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 5px;
|
|
background-color: #F1F1F1;
|
|
border-radius: 5px;
|
|
margin: 2px 0;
|
|
|
|
&>view:first-child {
|
|
margin-right: 5px;
|
|
|
|
image {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
}
|
|
|
|
&>view:nth-child(2) {
|
|
flex: 1;
|
|
}
|
|
}
|
|
}
|
|
</style> |