Files
jnpf_app/components/Jnpf/CandidateSelect/SelectPopup.vue
2026-01-22 11:01:19 +08:00

500 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<u-popup class="jnpf-tree-select-popup" mode="right" v-model="showPopup" width="100%" @close="close">
<view class="jnpf-tree-select-body">
<view class="jnpf-tree-select-title">
<text class="icon-ym icon-ym-report-icon-preview-pagePre backIcon" @tap="close()"></text>
<view class="title">选择用户</view>
</view>
<view class="jnpf-tree-select-search">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="handleSearch" bg-color="#f0f2f6" shape="square">
</u-search>
</view>
<view class="jnpf-tree-selected">
<view class="jnpf-tree-selected-head">
<view>{{$t('component.jnpf.common.selected')}}({{selectedList.length||0}})</view>
<view v-if="multiple" class="clear-btn" @click="setCheckAll">
{{$t('component.jnpf.common.clearAll')}}
</view>
</view>
<view class="jnpf-tree-selected-box">
<scroll-view scroll-y="true" class="select-list">
<u-tag closeable @close="delSelect(index)" v-for="(item,index) in selectedList" :key="index"
:text="item.nickName" class="u-selectTag" />
</scroll-view>
</view>
</view>
<view class="jnpf-tree-selected-line"></view>
<view class="jnpf-tree-selected-tabs">
<view class="tab-item" :class="{'tab-item-active':activeKey==='user'}" @click="toggloActive('user')">
用户
</view>
<block v-if="selectType === 'all'">
<!-- <view class="tab-item" :class="{'tab-item-active':activeKey==='position'}"
@click="toggloActive('position')">
组织岗位
</view> -->
<view class="tab-item" :class="{'tab-item-active':activeKey==='role'}"
@click="toggloActive('role')">
角色
</view>
<!-- <view class="tab-item" :class="{'tab-item-active':activeKey==='current'}"
@click="toggloActive('current')">
当前用户
</view> -->
</block>
</view>
<view class="jnpf-tree-selected-breadcrumb" v-if="selectType === 'all' && activeKey!=='current'">
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':!currStep}" @click="handleToFirst()"
v-if="activeKey==='user'">
用户组
</view>
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':!currStep}" @click="handleToFirst()"
v-if="activeKey==='position'">
组织岗位
</view>
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':!currStep}" @click="handleToFirst()"
v-if="activeKey==='role'">
角色
</view>
<view class="icon-ym icon-ym-caret-right breadcrumb-item" v-if="currStep"></view>
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':currStep}" v-if="currStep">
用户列表
</view>
</view>
<view class="jnpf-tree-select-tree">
<scroll-view :scroll-y="true" style="height: 100%" :scroll-top="scrollTop" @scroll="handleScroll"
v-show="(!hasPage && !currStep) && selectType === 'all' && activeKey!=='current'">
<ly-tree v-show="activeKey == 'user'" ref="tree" :props="realProps" :node-key="realProps.value"
:load="loadNode" lazy :tree-data="lazyOptions" show-node-icon :defaultExpandAll='false'
@node-click="handleTreeNodeClick" :show-checkbox="multiple" :show-radio="!multiple"
:expandOnClickNode="false" :checkOnClickNode="true" :expandOnCheckNode="false" checkStrictly />
<block v-if="activeKey == 'role'">
<view class="jnpf-selcet-list" v-if="list.length">
<view class="jnpf-selcet-cell" v-for="item in list" :key="item.id"
@click.stop="handleNodeClick(item)">
<view class="jnpf-selcet-cell-icon" :class='item.icon'></view>
<view class="jnpf-selcet-cell-name">
{{item.deptName}}
</view>
</view>
</view>
<Empty class="h-full" v-else />
</block>
</scroll-view>
<scroll-view :scroll-y="true" style="height: 100%" :refresher-enabled="false" :refresher-threshold="100"
:scroll-with-animation='true' :refresher-triggered="triggered" @scrolltolower="handleScrollToLower"
v-if="hasPage||currStep||activeKey==='current'">
<view class="jnpf-selcet-list" v-if="userList.length">
<view class="jnpf-selcet-cell" v-for="item in userList" :key="item.userId"
@click.stop="handleUserNodeClick(item)">
<view class="jnpf-selcet-cell-action">
<lyCheckbox :type="multiple ? 'checkbox' : 'radio'"
:checked="selectedIds.includes(item.userId)" />
</view>
<u-avatar class="jnpf-selcet-cell-avatar" :src="baseURL+item.headIcon" mode="circle"
size="44"></u-avatar>
<view class="jnpf-selcet-cell-name">
{{item.nickName}}
</view>
</view>
</view>
<Empty class="h-full" v-else />
</scroll-view>
</view>
<!-- 底部按钮 -->
<view class="jnpf-tree-select-actions">
<u-button class="buttom-btn" @click="close()">{{$t('common.cancelText')}}</u-button>
<u-button class="buttom-btn" type="primary"
@click.stop="handleConfirm()">{{$t('common.okText')}}</u-button>
</view>
</view>
</u-popup>
</template>
<script>
import {
getOrgAndPosSelector,
getUserList,
getSelectedUserList,
getOrganizeSelector,
getAdapterUserList,
getAdapterRoleList
} from '@/api/common'
import lyCheckbox from '@/components/ly-tree/components/ly-checkbox.vue';
import {
useBaseStore
} from '@/store/modules/base'
import Empty from '../Empty/index.vue'
const baseStore = useBaseStore()
const defaultProps = {
label: 'deptName',
value: 'deptId',
icon: 'icon',
children: 'children'
}
const defaultUserQuery = {
enabledMark: 1,
groupId: '',
organizeId: '',
positionId: '',
roleId: '',
};
export default {
props: {
selectedData: {
type: Array,
default: () => []
},
ableIds: {
type: Array,
default: () => []
},
selectType: {
type: String,
default: 'all'
},
// 通过双向绑定控制组件的弹出与收起
modelValue: {
type: Boolean,
default: false
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
props: {
type: Object,
default: () => ({
label: 'deptName',
value: 'deptId',
icon: 'icon',
children: 'children',
isLeaf: 'isLeaf'
})
},
multiple: {
type: Boolean,
default: false
}
},
components: {
lyCheckbox,
Empty
},
data() {
return {
moving: false,
selectedList: [],
selectedIds: [],
keyword: '',
showPopup: false,
lazyOptions: [{}],
activeKey: 'user',
hasPage: false,
pagination: {
hasPage: 1,
currentPage: 1,
pageSize: 20
},
triggered: false,
finish: false,
list: [],
userList: [],
scrollTop: 0,
currStep: 0,
userQuery: {},
};
},
watch: {
// 在select弹起的时候重新初始化所有数据
modelValue: {
handler(val) {
this.showPopup = val
if (val) setTimeout(() => this.init(), 10);
},
immediate: true
},
selectedList: {
handler(val) {
this.selectedIds = val.map((o) => o.userId);
this.$refs.tree && this.$refs.tree.setCheckedKeys(this.selectedIds)
},
deep: true
},
},
computed: {
baseURL() {
return this.define.baseURL
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
realProps() {
return {
...defaultProps,
...this.props
}
},
getCurrList() {
const userInfo = uni.getStorageSync('userInfo') || {}
const current = {
fullName: `${userInfo.userName}/${userInfo.userAccount}`,
headIcon: userInfo.headIcon,
id: userInfo.userId,
realName: userInfo.userName,
};
return [current];
}
},
methods: {
init() {
this.keyword = ""
this.hasPage = 0
this.currStep = 0
this.activeKey = 'user'
this.resetQuery()
this.$nextTick(() => {
this.triggered = true
})
this.getGroupList()
// this.selectedList = JSON.parse(JSON.stringify(this.selectedData))
// if (this.selectType === 'all') {
// this.getGroupList()
// } else {
// this.hasPage = 1
// this.getConditionOptions()
// }
},
handleToFirst() {
if (!this.currStep) return
this.currStep = 0
this.hasPage = false
this.keyword = ''
this.resetQuery()
},
resetQuery() {
this.userList = []
this.finish = false
this.pagination.currentPage = 1
},
getConditionOptions() {
if (!this.ableIds.length) return
const query = {
nickName: this.keyword,
ids: this.ableIds,
...this.pagination
}
getSelectedUserList(query).then(res => {
const list = res.data.list || []
if (list.length < this.pagination.pageSize) this.finish = true;
this.userList = this.userList.concat(list);
this.pagination.currentPage++
})
},
loadNode(node, resolve) {
const parentId = node.key || '0'
const level = node.level || 0
// 如果是根节点level为0直接使用已经构建好的options
// if (level === 0 && this.options.length > 0) {
// resolve(this.options)
// return
// }
getOrganizeSelector(this.selectType).then(res => {
const list = res.data || []
// 将返回的扁平数据转换为树形节点
const treeData = this.buildTree(list, 'deptId', 'deptPid', parentId)
console.log(treeData,'-treeData')
// 设置节点的isLeaf属性根据是否有子节点判断
treeData.forEach(item => {
// 判断该节点是否还有子节点(根据实际情况调整)
// 如果知道接口返回是否有子节点的字段,可以替换这个判断
item.isLeaf = !item.children || item.children.length === 0
})
console.log(`加载父节点 ${parentId} 的子节点:`, treeData)
resolve(treeData)
}).catch(() => {
resolve([]) // 异常时返回空数组,避免组件报错
})
},
handleScroll(e) {
this.scrollTop = e.detail.scrollTop
},
goTop() {
this.scrollTop = 0
},
handleScrollToLower() {
if (this.finish || this.activeKey === 'current') return
this.getUserData()
},
getUserData() {
this.selectType == 'all' ? this.getUserList() : this.getConditionOptions()
},
getUserList() {
let data = {
nickName: this.keyword,
// ...this.pagination,
...this.userQuery
}
getAdapterUserList(data).then(res => {
const list = res.data.records || []
if (list.length < this.pagination.pageSize) this.finish = true;
this.userList = this.userList.concat(list);
this.pagination.currentPage++
})
},
handleTreeNodeClick(item) {
const data = item.data
this.handleNodeClick(data)
},
handleNodeClick(data) {
const key = this.activeKey == 'user' ? 'deptId' : 'roleId'
this.hasPage = 1
this.userQuery = {
[key]: data[key],
fieldList: ["userId","nickName","sex","post","deptName"],
pageNo: this.hasPage,
pageSize: 10
};
this.currStep = 1
this.keyword = ''
this.resetQuery()
this.getUserList()
},
handleUserNodeClick(data) {
const index = this.selectedList.findIndex((o) => o.userId === data.userId);
if (index !== -1) return this.selectedList.splice(index, 1);
this.multiple ? this.selectedList.push(data) : (this.selectedList = [data]);
},
delSelect(index) {
this.selectedList.splice(index, 1);
},
setCheckAll() {
this.selectedIds = []
this.selectedList = []
},
handleConfirm() {
this.$emit('confirm', this.selectedList, this.selectedIds);
this.close();
},
close() {
this.$emit('close', false);
},
toggloActive(key) {
if (this.activeKey === key) return
this.currStep = 0
this.hasPage = false
this.keyword = ''
this.resetQuery()
this.$nextTick(async () => {
this.activeKey = key
this.goTop()
if (this.activeKey === 'user') return this.getGroupList()
if (this.activeKey === 'role') return this.getRoleList()
if (this.activeKey === 'current') this.userList = this.getCurrList
})
},
async getGroupList() {
const res = await getOrganizeSelector(this.selectType)
const list = res.data || []
const treeData = this.buildTree(list, 'deptId', 'deptPid', '0')
treeData.forEach(item => {
item.isLeaf = !item.children || item.children.length === 0
})
// this.list = treeData
this.lazyOptions = treeData
console.log(this.list,'list---')
// const list = this.formatDeptData(res.data) || []
// this.list = [{
// deptName: '全部用户',
// icon: 'icon-ym icon-ym-generator-group1',
// id: ''
// }, ...list]
},
async getRoleList() {
const res = await getAdapterRoleList()
const list = this.formatDeptData(res.data) || []
this.list = list
},
buildTree(data, idKey, pidKey, rootPid) {
const result = []
const map = {}
// 先构建ID映射
data.forEach(item => {
map[item[idKey]] = { ...item, children: [] }
})
// 组装父子关系
data.forEach(item => {
const parent = map[item[pidKey]]
if (item[pidKey] === rootPid) {
result.push(map[item[idKey]])
} else if (parent) {
parent.children.push(map[item[idKey]])
}
})
return result
},
formatDeptData(data) {
const result = [];
const formatItem = (item) => {
// 优先使用 deptId 字段(根据打印结果)
let deptId = item.deptId || item.roleId;
let deptName = item.deptName || item.roleName;
// 确保ID是字符串
deptId = String(deptId || '');
return {
...item,
deptId: deptId,
deptName: deptName ,
depthd: item.depthd,
depthName: item.depthName,
id: item.id
};
};
// 遍历数据
if (Array.isArray(data)) {
data.forEach(item => {
const formatted = formatItem(item);
if (formatted.deptId) {
result.push(formatted);
}
// 如果有子部门,递归处理
if (item.children && Array.isArray(item.children)) {
const children = this.formatDeptData(item.children);
result.push(...children);
}
});
}
return result;
},
handleSearch(val) {
this.keyword = val
this.hasPage = this.selectType !== 'all' || !!val
this.currStep = val ? 1 : 0
this.goTop()
if (this.activeKey != 'user') this.activeKey = 'user'
this.$nextTick(() => {
if (this.keyword || this.selectType !== 'all') {
this.userQuery = {
...defaultUserQuery
};
this.getGroupList()
this.resetQuery()
this.$u.debounce(this.getUserData, 300)
}
})
},
}
};
</script>