Files
jnpf_app/components/Jnpf/UsersSelect/SelectPopup.vue
2026-01-04 11:09:06 +08:00

447 lines
12 KiB
Vue
Raw Permalink 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.orgNameTree" 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>
<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==='group'}" @click="toggloActive('group')">
用户组
</view>
<view class="tab-item" :class="{'tab-item-active':activeKey==='system'}" @click="toggloActive('system')"
v-if="hasSys">
动态参数
</view>
</view>
<view class="jnpf-tree-selected-breadcrumb" v-if="['user','position'].includes(activeKey)">
<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="icon-ym icon-ym-caret-right breadcrumb-item" v-if="currStep"></view>
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':currStep}"
v-if="currStep && activeKey==='user'">
用户列表
</view>
<view class="breadcrumb-item" :class="{'breadcrumb-item-active':currStep}"
v-if="currStep && activeKey==='position'">
动态参数
</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) && ['user','position'].includes(activeKey)">
<ly-tree ref="tree" :props="realProps" :node-key="realProps.value" :load="loadNode" lazy
:tree-data="lazyOptions" show-node-icon :defaultExpandAll='false'
@node-click="handleTreeNodeClick" :expandOnClickNode="false" :checkOnClickNode="false"
v-if="activeKey==='position'" />
<block v-else>
<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.fullName}}
</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||!['user','position'].includes(activeKey)">
<view class="jnpf-selcet-list" v-if="userList.length">
<view class="jnpf-selcet-cell" v-for="item in userList" :key="item.id"
@click.stop="handleUserNodeClick(item)">
<view class="jnpf-selcet-cell-action">
<lyCheckbox :type="multiple ? 'checkbox' : 'radio'"
:checked="selectedIds.includes(item.id)" />
</view>
<u-avatar class="jnpf-selcet-cell-avatar" :src="baseURL+item.headIcon" mode="circle"
size="44" v-if="activeKey==='user'"></u-avatar>
<view class="jnpf-selcet-cell-name">
{{item.orgNameTree}}
</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
} from '@/api/common'
import lyCheckbox from '@/components/ly-tree/components/ly-checkbox.vue';
import Empty from '../Empty/index.vue'
import {
useBaseStore
} from '@/store/modules/base'
const baseStore = useBaseStore()
const defaultProps = {
label: 'fullName',
value: 'id',
icon: 'icon',
children: 'children'
}
const defaultUserQuery = {
enabledMark: 1,
groupId: '',
organizeId: '',
positionId: '',
roleId: '',
};
const systemFieldList = [{
fullName: '当前用户',
id: '@userId',
orgNameTree: '当前用户'
}];
const organizeSubList = [{
fullName: '当前组织',
id: '--org',
orgNameTree: ''
},
{
fullName: '当前组织及子组织',
id: '--subOrg',
orgNameTree: '及子组织'
},
{
fullName: '当前组织及子孙组织',
id: '--progenyOrg',
orgNameTree: '及子孙组织'
},
];
const positionSubList = [{
fullName: '当前岗位',
id: '--pos',
orgNameTree: ''
},
{
fullName: '当前岗位及子岗位',
id: '--subPos',
orgNameTree: '及子岗位'
},
{
fullName: '当前岗位及子孙岗位',
id: '--progenyPos',
orgNameTree: '及子孙岗位'
},
];
export default {
props: {
selectedData: {
type: Array,
default: () => []
},
// 通过双向绑定控制组件的弹出与收起
modelValue: {
type: Boolean,
default: false
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
props: {
type: Object,
default: () => ({
label: 'fullName',
value: 'id',
icon: 'icon',
children: 'children',
isLeaf: 'isLeaf'
})
},
multiple: {
type: Boolean,
default: true
},
hasSys: {
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.id);
this.$refs.tree && this.$refs.tree.setCheckedKeys(this.selectedIds)
},
deep: true
},
selectedData: {
handler(val) {
if (val) setTimeout(() => this.init(), 10);
},
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
}
},
},
methods: {
init() {
this.keyword = ""
this.hasPage = 0
this.currStep = 0
this.activeKey = 'user'
this.resetQuery()
this.$nextTick(() => {
this.triggered = true
})
this.selectedList = JSON.parse(JSON.stringify(this.selectedData))
this.getGroupList()
},
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
},
loadNode(node, resolve) {
const id = node.key || '0'
const type = node?.data?.type || 'organize'
const data = {
id,
type
}
getOrgAndPosSelector(data).then(res => {
const list = res.data?.list || []
resolve(list)
})
},
handleScroll(e) {
this.scrollTop = e.detail.scrollTop
},
goTop() {
this.scrollTop = 0
},
handleScrollToLower() {
if (this.finish || this.activeKey !== 'user') return
this.getUserList()
},
getUserList() {
let data = {
keyword: this.keyword,
...this.pagination,
...this.userQuery
}
getUserList(data).then(res => {
const list = (res.data.list || []).map((o) => ({
...o,
id: `${o.id}--user`,
orgNameTree: o.orgNameTree || o.fullName
}));
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.currStep = 1
this.getSubData(data)
},
getSubData(item) {
const subList = item.type === 'organize' ? organizeSubList : positionSubList;
const data = [];
for (const element of subList) {
data.push({
...item,
fullName: element.fullName,
icon: '',
id: `${item.id}${element.id}`,
orgNameTree: item.orgNameTree + element.orgNameTree
});
}
this.userList = data;
},
handleNodeClick(data) {
this.userQuery = {
...defaultUserQuery,
'groupId': data.id
};
this.currStep = 1
this.keyword = ''
this.resetQuery()
this.getUserList()
},
handleUserNodeClick(data) {
const index = this.selectedList.findIndex((o) => o.id === data.id);
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.keyword = ''
this.resetQuery()
this.$nextTick(async () => {
this.activeKey = key
this.goTop()
if (this.activeKey === 'group') {
const res = await baseStore.getGroupList()
this.userList = res.map((o) => ({
...o,
id: `${o.id}--group`,
orgNameTree: o.orgNameTree || o.fullName
}));
}
if (this.activeKey === 'role') {
const res = await baseStore.getRoleList()
this.userList = res.map((o) => ({
...o,
id: `${o.id}--role`,
orgNameTree: o.orgNameTree || o.fullName
}));
return
}
if (this.activeKey === 'system') this.userList = systemFieldList
})
},
async getGroupList() {
const list = await baseStore.getGroupList()
this.list = [{
fullName: '全部用户',
icon: 'icon-ym icon-ym-generator-group1',
id: ''
}, ...list]
},
handleSearch(val) {
this.keyword = val
this.hasPage = !!val
this.currStep = val ? 1 : 0
this.goTop()
if (this.activeKey != 'user') this.activeKey = 'user'
this.$nextTick(() => {
if (this.keyword) {
this.userQuery = {
...defaultUserQuery
};
this.getGroupList()
this.resetQuery()
this.$u.debounce(this.getUserList, 300)
}
})
},
}
};
</script>