diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index 87185c94..c52fcbff 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -65,6 +65,15 @@ public class SysUserController extends BaseController { return userService.selectPageUserList(user, pageQuery); } + /** + * 获取用户列表 通过or条件查询 + */ + @SaCheckPermission("system:user:list") + @GetMapping("/listByOr") + public TableDataInfo listByOr(SysUserBo user, PageQuery pageQuery) { + return userService.selectPageUserListByOr(user, pageQuery); + } + /** * 导出用户列表 */ diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java index 1c1a2bc8..ca7d0d54 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -20,11 +20,20 @@ public interface ISysUserService { * 根据条件分页查询用户列表 * * @param user 用户信息 - * @param pageQuery 发呢也 + * @param pageQuery 入参 * @return 用户信息 */ TableDataInfo selectPageUserList(SysUserBo user, PageQuery pageQuery); + /** + * 根据条件分页查询用户列表 通过or条件查询 + * + * @param user 用户信息 + * @param pageQuery 入参 + * @return 用户信息 + */ + TableDataInfo selectPageUserListByOr(SysUserBo user, PageQuery pageQuery); + /** * 导出用户列表 * diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 0195725b..a322e3d0 100644 --- a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -68,6 +68,112 @@ public class SysUserServiceImpl implements ISysUserService, UserService { return TableDataInfo.build(page); } + @Override + public TableDataInfo selectPageUserListByOr(SysUserBo user, PageQuery pageQuery) { + Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapperByOr(user)); + return TableDataInfo.build(page); + } + + private Wrapper buildQueryWrapperByOr(SysUserBo user) { + Map params = user.getParams(); + QueryWrapper wrapper = Wrappers.query(); + + // 基础条件,必须保持AND + wrapper.eq("u.del_flag", SystemConstants.NORMAL); + wrapper.eq("u.status", '0'); + + // 检查是否有任何查询条件 + boolean hasAnyCondition = ObjectUtil.isNotNull(user.getUserId()) + || StringUtils.isNotBlank(user.getUserIds()) + || StringUtils.isNotBlank(user.getUserName()) + || StringUtils.isNotBlank(user.getNickName()) + // || StringUtils.isNotBlank(user.getStatus()) + || StringUtils.isNotBlank(user.getPhonenumber()) + || (params.get("beginTime") != null && params.get("endTime") != null) + || ObjectUtil.isNotNull(user.getDeptId()); + + // 只有当存在查询条件时,才构建OR条件组合 + if (hasAnyCondition) { + wrapper.and(w -> { + boolean hasCondition = false; + + // 用户ID条件 + if (ObjectUtil.isNotNull(user.getUserId())) { + w.eq("u.user_id", user.getUserId()); + hasCondition = true; + } + + // 用户ID串条件 + if (StringUtils.isNotBlank(user.getUserIds())) { + if (hasCondition) + w.or(); + w.in("u.user_id", StringUtils.splitTo(user.getUserIds(), Convert::toLong)); + hasCondition = true; + } + + // 用户名模糊查询条件 + if (StringUtils.isNotBlank(user.getUserName())) { + if (hasCondition) + w.or(); + w.like("u.user_name", user.getUserName()); + hasCondition = true; + } + + // 昵称模糊查询条件 + if (StringUtils.isNotBlank(user.getNickName())) { + if (hasCondition) + w.or(); + w.like("u.nick_name", user.getNickName()); + hasCondition = true; + } + + // 状态条件 + // if (StringUtils.isNotBlank(user.getStatus())) { + // if (hasCondition) + // w.or(); + // w.eq("u.status", user.getStatus()); + // hasCondition = true; + // } + + // 手机号模糊查询条件 + if (StringUtils.isNotBlank(user.getPhonenumber())) { + if (hasCondition) + w.or(); + w.like("u.phonenumber", user.getPhonenumber()); + hasCondition = true; + } + + // 创建时间范围条件 + if (params.get("beginTime") != null && params.get("endTime") != null) { + if (hasCondition) + w.or(); + w.between("u.create_time", params.get("beginTime"), params.get("endTime")); + hasCondition = true; + } + + // 部门ID条件(包括子部门) + if (ObjectUtil.isNotNull(user.getDeptId())) { + if (hasCondition) + w.or(); + List deptList = deptMapper.selectListByParentId(user.getDeptId()); + List ids = StreamUtils.toList(deptList, SysDept::getDeptId); + ids.add(user.getDeptId()); + w.in("u.dept_id", ids); + } + }); + } + + // 排除用户ID条件,保持AND + if (StringUtils.isNotBlank(user.getExcludeUserIds())) { + wrapper.notIn("u.user_id", StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); + } + + // 排序 + wrapper.orderByAsc("u.user_id"); + + return wrapper; + } + /** * 根据条件分页查询用户列表 * diff --git a/mobile/src/components/Process/approvalRecord.vue b/mobile/src/components/Process/approvalRecord.vue index 89373740..9f5074c4 100644 --- a/mobile/src/components/Process/approvalRecord.vue +++ b/mobile/src/components/Process/approvalRecord.vue @@ -6,8 +6,9 @@ :custom-style="{ width: '95vw', height: '85vh', - maxWidth: '800px', - maxHeight: '700px', + maxWidth: '95vw', + maxHeight: '85vh', + overflow: 'auto', }" @close="handleClose" > diff --git a/mobile/src/components/Process/submitVerify.vue b/mobile/src/components/Process/submitVerify.vue index 87374eda..c9a4e92b 100644 --- a/mobile/src/components/Process/submitVerify.vue +++ b/mobile/src/components/Process/submitVerify.vue @@ -7,7 +7,7 @@ height="90%" border-radius="12" @close="cancel" - :custom-style="{ width: '90vw' }" + :custom-style="{ width: '90vw', maxHeight: '90vh', overflow: 'auto' }" > @@ -248,33 +248,37 @@ width="80%" height="70%" border-radius="12" - @close="backVisible = false" + z-index="20080" + @close="closeBackDialog" :custom-style="{ width: '90vw' }" > 驳回 - + - - 驳回节点 - - - + + 驳回节点 + + + + + {{ selectedBackNodeName || '请选择驳回节点' }} + + + + @@ -342,7 +346,7 @@ 取消 @@ -359,6 +363,7 @@ width="90%" height="60%" border-radius="12" + z-index="20080" @close="deleteSignatureVisible = false" > @@ -425,6 +430,8 @@ export default { backButtonDisabled: true, taskNodeList: [], selectedBackNodeName: '', + selectedBackNodeIndex: 0, + showBackNodePicker: false, nickName: {}, nodeCode: '', buttonObj: { @@ -650,6 +657,7 @@ export default { this.backLoading = false; this.backButtonDisabled = false; if (this.taskNodeList.length > 0) { + this.selectedBackNodeIndex = 0; this.backForm.nodeCode = this.taskNodeList[0].nodeCode; this.selectedBackNodeName = this.taskNodeList[0].nodeName; } @@ -663,11 +671,26 @@ export default { } }, - // 退回节点变化 - handleBackNodeChange(e) { - const index = e.detail.value; - this.backForm.nodeCode = this.taskNodeList[index].nodeCode; - this.selectedBackNodeName = this.taskNodeList[index].nodeName; + // 确认选择驳回节点 + onBackNodeConfirm(e) { + const { indexs, value } = e; + this.selectedBackNodeIndex = indexs[0]; + this.backForm.nodeCode = value[0].nodeCode; + this.selectedBackNodeName = value[0].nodeName; + this.showBackNodePicker = false; + }, + + // 取消选择驳回节点 + onBackNodeClose() { + this.showBackNodePicker = false; + }, + + // 关闭退回弹窗 + closeBackDialog() { + this.backVisible = false; + this.showBackNodePicker = false; + this.selectedBackNodeIndex = 0; + this.selectedBackNodeName = ''; }, // 退回流程 @@ -689,7 +712,7 @@ export default { if (response.code === 200) { this.visible = false; - this.backVisible = false; + this.closeBackDialog(); this.$emit('submitCallback'); uni.showToast({ title: '操作成功', @@ -1055,21 +1078,30 @@ export default { // 显示确认对话框 showConfirm(content) { return new Promise((resolve) => { - // 临时隐藏主弹窗,避免层级冲突 + // 临时隐藏所有弹窗,避免层级冲突 const originalVisible = this.visible; + const originalBackVisible = this.backVisible; + const originalDeleteSignatureVisible = this.deleteSignatureVisible; + this.visible = false; + this.backVisible = false; + this.deleteSignatureVisible = false; uni.showModal({ title: '提示', content, success: (res) => { - // 恢复主弹窗显示状态 + // 恢复所有弹窗显示状态 this.visible = originalVisible; + this.backVisible = originalBackVisible; + this.deleteSignatureVisible = originalDeleteSignatureVisible; resolve(res); }, fail: () => { - // 恢复主弹窗显示状态 + // 恢复所有弹窗显示状态 this.visible = originalVisible; + this.backVisible = originalBackVisible; + this.deleteSignatureVisible = originalDeleteSignatureVisible; resolve({ confirm: false }); }, }); @@ -1146,6 +1178,12 @@ export default { display: flex; flex-wrap: wrap; gap: 16rpx; + max-height: 200rpx; + overflow-y: auto; + padding: 8rpx; + border: 1rpx solid #e6e6e6; + border-radius: 8rpx; + background-color: #fafafa; } .copy-tag { @@ -1300,4 +1338,50 @@ export default { padding: 0 24rpx; font-size: 24rpx; } + +.picker-input { + display: flex; + align-items: center; + justify-content: space-between; + height: 80rpx; + padding: 0 24rpx; + background-color: #f8f9fa; + border: 1rpx solid #e6e6e6; + border-radius: 8rpx; + cursor: pointer; +} + +.picker-input:active { + background-color: #f0f0f0; +} + +.picker-text { + flex: 1; + font-size: 28rpx; + color: #333; +} + +.picker-text:empty::before { + content: attr(placeholder); + color: #999; +} + +.horizontal-item { + display: flex; + align-items: center; + gap: 20rpx; +} + +.horizontal-label { + flex-shrink: 0; +} + +.horizontal-control { + flex: 1; + width: 100%; +} + +.horizontal-control .picker-input { + width: 80% !important; +} diff --git a/mobile/src/components/UserSelect/index.vue b/mobile/src/components/UserSelect/index.vue index 52eed807..0fbc95a7 100644 --- a/mobile/src/components/UserSelect/index.vue +++ b/mobile/src/components/UserSelect/index.vue @@ -6,7 +6,7 @@ height="85%" border-radius="12" @close="close" - :custom-style="{ width: '90vw' }" + :custom-style="{ width: '90vw', maxHeight: '90vh', overflow: 'auto' }" > @@ -34,7 +34,11 @@ @@ -171,7 +175,7 @@ export default { // 初始化数据 async initData() { try { - await this.getDeptList(); + // await this.getDeptList(); await this.getUserList(); await this.initSelectUser(); } catch (error) { @@ -223,6 +227,8 @@ export default { this.loadStatus = 'loading'; } + this.queryParams.status = 0; + try { // 设置查询参数 this.queryParams.userIds = this.userIds; @@ -232,22 +238,24 @@ export default { this.queryParams.userName = ''; } else { this.queryParams.userName = this.searchKeyword; + this.queryParams.nickName = this.searchKeyword; this.queryParams.phonenumber = ''; } } else { this.queryParams.userName = ''; this.queryParams.phonenumber = ''; + this.queryParams.nickName = ''; } // 设置部门ID - if (this.currentDeptIndex > 0) { - this.queryParams.deptId = this.deptList[this.currentDeptIndex].value; - } else { - this.queryParams.deptId = ''; - } + // if (this.currentDeptIndex > 0) { + // this.queryParams.deptId = this.deptList[this.currentDeptIndex].value; + // } else { + // this.queryParams.deptId = ''; + // } const response = await request({ - url: '/system/user/list', + url: '/system/user/listByOr', method: 'GET', data: this.queryParams, }); @@ -290,6 +298,7 @@ export default { // 搜索处理 handleSearch() { + console.log('执行到此-------------------'); this.getUserList(); }, @@ -364,14 +373,15 @@ export default { // 初始化已选用户 async initSelectUser() { + console.log('this.data:', this.data); if (this.data) { const userIds = this.computedIds(this.data); if (userIds.length > 0) { try { const response = await request({ - url: '/system/user/optionselect', - method: 'POST', - data: userIds, + url: '/system/user/optionselect?userIds=' + userIds, + method: 'GET', + // data: userIds, }); if (response.code === 200) { @@ -441,10 +451,16 @@ export default { border-bottom: 1rpx solid #f0f0f0; max-height: 200rpx; overflow-y: auto; + display: flex; + flex-wrap: wrap; + align-items: center; } .user-tag { margin: 8rpx 16rpx 8rpx 0; + display: inline-block; + width: auto; + flex-shrink: 0; } .search-section { diff --git a/mobile/src/pages/index/index.vue b/mobile/src/pages/index/index.vue index f350406b..74d8918d 100644 --- a/mobile/src/pages/index/index.vue +++ b/mobile/src/pages/index/index.vue @@ -76,6 +76,7 @@ + - + - + + + + @@ -224,6 +236,15 @@ export default { userList: [], // 用户列表 userSearchKeyword: '', // 用户搜索关键词 userSearchTimer: null, // 搜索防抖定时器 + + // 用户列表分页参数 + userListParams: { + pageNum: 1, + pageSize: 20, + total: 0, + }, + userListLoading: false, // 用户列表加载状态 + userLoadmoreStatus: 'loadmore', // 用户列表加载更多状态 }; }, @@ -459,6 +480,11 @@ export default { // 打开申请人选择 openUserSelect() { this.userSelectDialog.visible = true; + // 重置分页数据 + this.userListParams.pageNum = 1; + this.userListParams.total = 0; + this.userLoadmoreStatus = 'loadmore'; + this.userSearchKeyword = ''; this.loadUserList(); }, @@ -466,6 +492,10 @@ export default { cancelUserSelect() { this.userSelectDialog.visible = false; this.userSearchKeyword = ''; + // 重置用户列表分页数据 + this.userListParams.pageNum = 1; + this.userListParams.total = 0; + this.userLoadmoreStatus = 'loadmore'; }, // 确认申请人选择 @@ -505,40 +535,90 @@ export default { }, // 加载用户列表 - async loadUserList() { + async loadUserList(isLoadMore = false) { + if (!isLoadMore) { + this.userListLoading = true; + this.userListParams.pageNum = 1; + } else { + this.userLoadmoreStatus = 'loading'; + } + try { const res = await request({ - url: '/system/user/list', + url: '/system/user/listByOr', method: 'GET', params: { - pageNum: 1, - pageSize: 100, + pageNum: this.userListParams.pageNum, + pageSize: this.userListParams.pageSize, + status: '0', userName: this.userSearchKeyword || undefined, nickName: this.userSearchKeyword || undefined, }, }); if (res && res.code === 200) { - // 每次加载用户列表时,所有用户默认都不选中 - this.userList = (res.rows || []).map((user) => ({ + const newUsers = (res.rows || []).map((user) => ({ ...user, selected: false, })); - console.log('加载用户列表完成,用户数量:', this.userList.length); + + if (isLoadMore) { + // 加载更多,追加数据 + this.userList = [...this.userList, ...newUsers]; + // 判断是否还有更多数据 + if (newUsers.length < this.userListParams.pageSize) { + this.userLoadmoreStatus = 'nomore'; + } else { + this.userLoadmoreStatus = 'loadmore'; + } + } else { + // 首次加载或搜索,替换数据 + this.userList = newUsers; + this.userListParams.total = res.total || 0; + // 重置加载更多状态 + if (this.userList.length >= this.userListParams.total) { + this.userLoadmoreStatus = 'nomore'; + } else { + this.userLoadmoreStatus = 'loadmore'; + } + } + + console.log( + '加载用户列表完成,当前用户数量:', + this.userList.length, + '总数量:', + this.userListParams.total, + ); } else { - this.userList = []; + if (!isLoadMore) { + this.userList = []; + this.userListParams.total = 0; + } uni.showToast({ title: res?.msg || '获取用户列表失败', icon: 'none', }); + if (isLoadMore) { + this.userLoadmoreStatus = 'loadmore'; + } } } catch (error) { console.error('获取用户列表失败:', error); - this.userList = []; + if (!isLoadMore) { + this.userList = []; + this.userListParams.total = 0; + } uni.showToast({ title: '获取用户列表失败', icon: 'none', }); + if (isLoadMore) { + this.userLoadmoreStatus = 'loadmore'; + } + } finally { + if (!isLoadMore) { + this.userListLoading = false; + } } }, @@ -549,10 +629,24 @@ export default { } this.userSearchTimer = setTimeout(() => { + // 搜索时重置分页数据 + this.userListParams.pageNum = 1; + this.userListParams.total = 0; + this.userLoadmoreStatus = 'loadmore'; this.loadUserList(); }, 500); }, + // 加载更多用户 + loadMoreUsers() { + if (this.userLoadmoreStatus === 'nomore' || this.userLoadmoreStatus === 'loading') { + return; + } + + this.userListParams.pageNum++; + this.loadUserList(true); + }, + // 获取头像显示文字(取前两个字符) getAvatarText(nickName) { if (!nickName) return ''; @@ -769,6 +863,12 @@ export default { } } +// 用户列表的loadmore样式 +::v-deep .u-loadmore { + padding: 20rpx 0; + font-size: 24rpx; +} + .user-info { display: flex; align-items: center; diff --git a/mobile/src/pages/xg/xsjcxx/xsqj/leaveEdit/index.vue b/mobile/src/pages/xg/xsjcxx/xsqj/leaveEdit/index.vue index 2fffc70a..d0f19e43 100644 --- a/mobile/src/pages/xg/xsjcxx/xsqj/leaveEdit/index.vue +++ b/mobile/src/pages/xg/xsjcxx/xsqj/leaveEdit/index.vue @@ -3050,7 +3050,15 @@ export default { return false; } - // 1. 校验是否需要上传地理位置 + // 1. 校验请假时间 + const startTime = new Date(this.form.startTime); + const endTime = new Date(this.form.endTime); + if (endTime.getTime() <= startTime.getTime()) { + uni.showToast({ title: '请假结束时间必须大于开始时间', icon: 'none' }); + return false; + } + + // 2. 校验是否需要上传地理位置 if (leaveType.needLocationOnApply === '1') { if (!this.form.leaveLocation) { uni.showToast({ title: '该请假类型需要上传地理位置', icon: 'none' }); @@ -3058,7 +3066,7 @@ export default { } } - // 2. 校验和设置销假状态 + // 3. 校验和设置销假状态 if (leaveType.needCancelForNonLeave === '0' && this.form.isLeaveSchool === '0') { // 非离校请假且该类型不需要销假时,自动设置为不需要销假 this.form.needCancel = '0'; @@ -3067,7 +3075,7 @@ export default { this.form.needCancel = '1'; } - // 3. 校验是否可以提前请假 + // 4. 校验是否可以提前请假 if (leaveType.canApplyInAdvance === '0') { const now = new Date(); now.setHours(23, 59, 59, 999); @@ -3078,7 +3086,7 @@ export default { } } - // 4. 校验最大申请时长 + // 5. 校验最大申请时长 if (leaveType.maxApplyDuration && leaveType.maxApplyDuration > 0) { const start = new Date(this.form.startTime); const end = new Date(this.form.endTime);