Files
im-uniapp/components/oa/oa-user-select/index.vue

223 lines
5.7 KiB
Vue
Raw Normal View History

2025-07-24 15:45:18 +08:00
<template>
<view>
<!-- 已选用户展示 -->
<view v-if="multi ? selectedUsers.length : selectedUser" class="selected-user">
<template v-if="multi">
<view v-for="user in selectedUsers" :key="user.userId" class="user-tag">
<text>{{ user.nickName }}</text>
<text class="remove-btn" @click="removeUser(user)">×</text>
</view>
</template>
<template v-else>
<text>{{ selectedUser.nickName }}</text>
<text class="remove-btn" @click="clearUser">×</text>
</template>
</view>
<button type="primary" size="mini" @click="openPopup">{{ multi ? '选择人员' : '选择负责人' }}</button>
<uni-popup ref="popup" type="center">
<view class="user-select-popup">
<view class="popup-header">
<text>{{ multi ? '选择人员' : '选择负责人' }}</text>
<text class="close-btn" @click="closePopup">×</text>
</view>
<view style="padding: 16rpx;">
<input
v-model="userSearchKeyword"
placeholder="请输入昵称搜索"
class="form-input"
style="width: 100%;"
/>
</view>
<view style="height: 400rpx; overflow-y: auto;">
<uni-data-checkbox
:multiple="multi"
v-model="innerValue"
:localdata="filteredUserList"
:map="{text:'nickName', value:'userId'}"
selectedColor="#409EFF"
/>
<view v-if="filteredUserList.length === 0" style="text-align:center;color:#999;padding:20rpx;">
暂无匹配用户
</view>
</view>
<view class="popup-footer">
<button type="primary" size="mini" @click="confirmSelect" :disabled="multi ? !innerValue.length : !innerValue">确定</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { selectUser } from '@/api/oa/user';
export default {
name: 'OaUserSelect',
props: {
value: {
type: [String, Number, Array],
default: ''
},
multi: {
type: Boolean,
default: false
}
},
data() {
return {
userList: [],
userSearchKeyword: '',
innerValue: this.multi ? [] : '',
selectedUser: null,
selectedUsers: []
};
},
computed: {
filteredUserList() {
if (!this.userSearchKeyword.trim()) return this.userList;
return this.userList.filter(user =>
user.nickName && user.nickName.includes(this.userSearchKeyword.trim())
);
}
},
watch: {
value: {
immediate: true,
handler(val) {
this.innerValue = this.multi ? (Array.isArray(val) ? val : []) : (val || '');
if (this.multi) {
this.selectedUsers = this.userList.filter(u => this.innerValue.includes(u.userId));
} else {
this.selectedUser = this.userList.find(u => u.userId === this.innerValue) || null;
}
}
},
userList(val) {
if (this.multi) {
this.selectedUsers = val.filter(u => this.innerValue.includes(u.userId));
} else {
this.selectedUser = val.find(u => u.userId === this.innerValue) || null;
}
},
multi(val) {
// 切换单多选时重置
this.innerValue = val ? [] : '';
}
},
methods: {
async openPopup() {
if (!this.userList.length) {
const res = await selectUser({ pageNum: 1, pageSize: 999 });
this.userList = res.rows || [];
}
// innerValue已由watch同步
this.$refs.popup.open();
},
closePopup() {
this.$refs.popup.close();
},
selectUser(user) {
if (this.multi) {
const idx = this.selectedUsers.findIndex(u => u.userId === user.userId);
if (idx > -1) {
this.selectedUsers.splice(idx, 1);
} else {
this.selectedUsers.push(user);
}
} else {
this.tempUser = user;
}
},
isSelected(user) {
if (this.multi) {
return this.selectedUsers.some(u => u.userId === user.userId);
} else {
return this.value === user.userId;
}
},
confirmSelect() {
this.$emit('input', this.innerValue);
if (this.multi) {
this.selectedUsers = this.userList.filter(u => this.innerValue.includes(u.userId));
} else {
this.selectedUser = this.userList.find(u => u.userId === this.innerValue) || null;
}
this.closePopup();
},
clearUser() {
this.$emit('input', this.multi ? [] : '');
if (this.multi) {
this.selectedUsers = [];
} else {
this.selectedUser = null;
}
},
removeUser(user) {
if (this.multi) {
this.innerValue = this.innerValue.filter(id => id !== user.userId);
this.selectedUsers = this.selectedUsers.filter(u => u.userId !== user.userId);
this.$emit('input', this.innerValue);
}
}
}
};
</script>
<style scoped>
.selected-user {
display: flex;
align-items: center;
gap: 8rpx;
margin-bottom: 8rpx;
}
.remove-btn {
color: #ff4757;
margin-left: 8rpx;
font-size: 28rpx;
cursor: pointer;
}
.user-select-popup {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
width: 600rpx;
max-width: 90vw;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 30rpx;
font-weight: bold;
margin-bottom: 16rpx;
}
.close-btn {
color: #888;
font-size: 36rpx;
cursor: pointer;
}
.user-list {
margin-top: 8rpx;
}
.user-row {
display: flex;
align-items: center;
gap: 16rpx;
padding: 12rpx 0;
border-bottom: 1rpx solid #f0f0f0;
cursor: pointer;
}
.user-row.selected {
background: #e6fffb;
}
.user-dept {
color: #999;
font-size: 24rpx;
margin-left: auto;
}
.popup-footer {
display: flex;
justify-content: flex-end;
margin-top: 16rpx;
}
</style>