From 536e54062ea959d09b5218b6f51069b34c09279b Mon Sep 17 00:00:00 2001 From: preschooler Date: Tue, 5 May 2026 12:35:31 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(system):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E9=80=89=E6=8B=A9=20UserSelectV2=20=E5=B8=83?= =?UTF-8?q?=E5=B1=80=EF=BC=8C=E5=A4=9A=E9=80=89=E6=94=AF=E6=8C=81=E3=80=81?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=80=89=E4=B8=AD=E5=BD=93=E5=89=8D=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=94=AF=E6=8C=81=E3=80=81=E7=A6=81=E9=80=89=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E3=80=81=E9=BB=98=E8=AE=A4=E9=83=A8=E9=97=A8=E6=94=AF?= =?UTF-8?q?=E6=8C=81=EF=BC=8C=E5=8F=AF=E6=9B=BF=E4=BB=A3=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E6=89=80=E6=9C=89=E4=BD=8D=E7=BD=AE=EF=BC=8C=E5=8F=AF=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E5=8E=9F=20UserSelectForm=E3=80=81UserSelect=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E4=B8=80=E6=AC=A1=E6=80=A7=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E6=89=80=E6=9C=89=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/system/user/index.ts | 6 + .../system/dept/components/DeptTreeSelect.vue | 39 +++--- .../user/components/UserSelectDialogV2.vue | 82 ++++++++--- .../system/user/components/UserSelectV2.vue | 127 +++++++++++++----- 4 files changed, 189 insertions(+), 65 deletions(-) diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts index 36776ea24..e92a24ae6 100644 --- a/src/api/system/user/index.ts +++ b/src/api/system/user/index.ts @@ -15,6 +15,7 @@ export interface UserVO { remark: string loginDate: Date createTime: Date + disabled?: boolean } // 查询用户管理列表 @@ -22,6 +23,11 @@ export const getUserPage = (params: PageParam) => { return request.get({ url: '/system/user/page', params }) } +// 查询用户管理列表 +export const getUserList = (ids: number[]) => { + return request.get({ url: '/system/user/list', params: { ids: ids.join(',') } }) +} + // 查询用户详情 export const getUser = (id: number) => { return request.get({ url: '/system/user/get?id=' + id }) diff --git a/src/views/system/dept/components/DeptTreeSelect.vue b/src/views/system/dept/components/DeptTreeSelect.vue index 46fb0b94a..08642a338 100644 --- a/src/views/system/dept/components/DeptTreeSelect.vue +++ b/src/views/system/dept/components/DeptTreeSelect.vue @@ -10,28 +10,25 @@ reset() — 清空选中状态(供外部重置按钮调用) --> @@ -98,7 +95,11 @@ const reset = () => { treeRef.value?.setCurrentKey(undefined) } -defineExpose({ reset }) +const setCurrent = (deptId: number) => { + treeRef.value?.setCurrentKey(deptId) +} + +defineExpose({ reset, setCurrent }) /** 初始化 */ onMounted(async () => { diff --git a/src/views/system/user/components/UserSelectDialogV2.vue b/src/views/system/user/components/UserSelectDialogV2.vue index dfc9ec623..9c9898527 100644 --- a/src/views/system/user/components/UserSelectDialogV2.vue +++ b/src/views/system/user/components/UserSelectDialogV2.vue @@ -6,31 +6,41 @@ Props: multiple — true 多选(checkbox),false 单选(radio);默认 true + deptId — 部门 ID Events: selected(rows: UserVO[]) — 确认选择后触发,单选时数组长度为 1 Expose: open(selectedIds?: number[]) — 打开弹窗,可传入已选 ID 用于预选高亮 --> + @@ -109,6 +126,13 @@ + (), { + title: '人员选择', multiple: true } ) const message = useMessage() const emit = defineEmits<{ - selected: [rows: UserApi.UserVO[]] + selected: [rows: UserApi.UserVO[], activityId?: any] }>() const dialogVisible = ref(false) // 弹窗是否展示 const loading = ref(false) // 列表加载中 const list = ref([]) // 用户列表 const total = ref(0) // 总条数 +const activityId = ref() // ==================== 部门树 ==================== const deptTreeRef = ref() // 部门树 Ref @@ -168,6 +197,12 @@ const selectedRows = ref([]) // 多选模式:选中行 const selectedRadioId = ref() // 单选模式:选中 ID const currentRadioRow = ref() // 单选模式:选中行对象 const preSelectedIds = ref([]) // 打开弹窗时传入的已选 ID +const preDisabledIds = ref([]) // 打开弹窗时传入的禁选 ID + +/** 多选:是否可以选中 */ +const selectable = (row: UserApi.UserVO) => { + return !preDisabledIds.value.includes(row.id) +} /** 多选:checkbox 变化 */ const handleSelectionChange = (rows: UserApi.UserVO[]) => { @@ -183,6 +218,9 @@ const handleRadioChange = (row: UserApi.UserVO) => { /** 单击行:单选模式下点击整行即选中(降低操作成本),多选不处理(避免和 dblclick 冲突) */ const handleRowClick = (row: UserApi.UserVO) => { + if (row.disabled) { + return + } if (props.multiple) { return } @@ -192,6 +230,9 @@ const handleRowClick = (row: UserApi.UserVO) => { /** 双击行:多选模式切换勾选,单选模式直接确认 */ const handleRowDblClick = (row: UserApi.UserVO) => { + if (row.disabled) { + return + } if (props.multiple) { tableRef.value?.toggleRowSelection(row) return @@ -206,6 +247,7 @@ const queryParams = reactive({ pageNo: 1, // 页码 pageSize: 10, // 每页条数 username: undefined as string | undefined, // 用户名称 + nickname: undefined as string | undefined, // 用户昵称 mobile: undefined as string | undefined, // 手机号码 status: CommonStatusEnum.ENABLE as number | undefined, // 状态:默认只查启用 deptId: undefined as number | undefined // 部门 ID(从左侧树选择) @@ -218,6 +260,9 @@ const getList = async () => { const data = await UserApi.getUserPage(queryParams) list.value = data.list total.value = data.total + list.value.forEach((row) => { + row.disabled = preDisabledIds.value.includes(row.id) + }) await nextTick() applyPreSelection() } finally { @@ -273,13 +318,13 @@ const confirmSelect = () => { message.warning('请至少选择一条数据') return } - emit('selected', selectedRows.value) + emit('selected', selectedRows.value, activityId.value) } else { if (!currentRadioRow.value) { message.warning('请选择一条数据') return } - emit('selected', [currentRadioRow.value]) + emit('selected', [currentRadioRow.value], activityId.value) } dialogVisible.value = false } @@ -287,24 +332,29 @@ const confirmSelect = () => { // ==================== 打开弹窗 ==================== /** 打开弹窗,可传入已选 ID 用于预选高亮 */ -const open = async (selectedIds?: number[]) => { +const open = async (selectedIds?: number[], disabledIds?: number[], _activityId?: any) => { + preDisabledIds.value = disabledIds ?? [] + activityId.value = _activityId dialogVisible.value = true // 重置查询条件 + 页码,避免二次打开继承上次过滤上下文 queryParams.username = undefined queryParams.mobile = undefined queryParams.status = CommonStatusEnum.ENABLE - queryParams.deptId = undefined + queryParams.deptId = props.deptId queryParams.pageNo = 1 // 清空上一次的选中状态 selectedRows.value = [] selectedRadioId.value = undefined currentRadioRow.value = undefined - preSelectedIds.value = selectedIds ?? [] + preSelectedIds.value = (selectedIds ?? []).filter((id) => !preDisabledIds.value.includes(id)) // 清空部门树选中 + 多选模式清空跨页缓存的勾选 await nextTick() deptTreeRef.value?.reset() tableRef.value?.clearSelection() await getList() + if (queryParams.deptId) { + deptTreeRef.value?.setCurrent(queryParams.deptId) + } } defineExpose({ open }) diff --git a/src/views/system/user/components/UserSelectV2.vue b/src/views/system/user/components/UserSelectV2.vue index 79cb3dc35..942937c89 100644 --- a/src/views/system/user/components/UserSelectV2.vue +++ b/src/views/system/user/components/UserSelectV2.vue @@ -3,15 +3,19 @@ 对齐 MdVendorSelect 架构模式 - 交互:显示为只读 el-input,点击打开弹窗(单选模式)进行选择 + 交互:显示为只读 el-input,点击打开弹窗进行选择 Props: modelValue — 绑定的用户 ID(v-model) + defaultCurrentUser — 默认选中当前用户 + multiple — 默认 false disabled — 是否禁用 + disabledIds — 禁用的用户 ID clearable — 是否允许清空(鼠标悬停时显示清除图标) placeholder — 占位文字 + deptId — 部门 ID Events: update:modelValue — v-model 更新 - change(item) — 选中用户变化时触发,传递完整 UserVO(清空时为 undefined) + change(item | items) — 选中用户变化时触发,传递完整 UserVO(清空时为 undefined | []) -->