|
|
@@ -1,5 +1,5 @@
|
|
|
<template>
|
|
|
- <Dialog v-model="dialogVisible" title="分配用户">
|
|
|
+ <Dialog v-model="dialogVisible" title="分配用户" width="780">
|
|
|
<el-form ref="formRef" v-loading="formLoading" :model="formData" label-width="80px">
|
|
|
<el-form-item label="角色名称">
|
|
|
<el-tag>{{ formData.name }}</el-tag>
|
|
|
@@ -7,34 +7,17 @@
|
|
|
<el-form-item label="角色标识">
|
|
|
<el-tag>{{ formData.code }}</el-tag>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="选择用户">
|
|
|
- <el-input
|
|
|
- v-model="userSearch.nickName"
|
|
|
- clearable
|
|
|
- placeholder="请输入用户名搜索"
|
|
|
- class="mb-14px"
|
|
|
- @keyup.enter="fetchUserList"
|
|
|
- >
|
|
|
- <template #append>
|
|
|
- <el-button @click="fetchUserList">
|
|
|
- <Icon icon="ep:search" />
|
|
|
- </el-button>
|
|
|
- </template>
|
|
|
- </el-input>
|
|
|
- <SmartTable
|
|
|
- ref="userTableRef"
|
|
|
- v-model:pageNo="userSearch.pageNo"
|
|
|
- v-model:pagesize="userSearch.pageSize"
|
|
|
- :total="userSearch.total"
|
|
|
- :columns="userColumns"
|
|
|
- :data="userTableData"
|
|
|
- :buttons="[]"
|
|
|
- :showSettingTools="false"
|
|
|
- :showSearch="false"
|
|
|
- :showRefresh="false"
|
|
|
- @on-page-size-change="onPageSizeChange"
|
|
|
- @on-page-no-change="onPageNoChange"
|
|
|
- />
|
|
|
+ <el-form-item label="选择用户" class="transfer-form-item">
|
|
|
+ <div class="transfer-wrapper">
|
|
|
+ <el-transfer
|
|
|
+ v-model="selectedUserIdList"
|
|
|
+ :titles="['未选用户', '已选用户']"
|
|
|
+ filterable
|
|
|
+ filter-placeholder="搜索用户"
|
|
|
+ :data="userList"
|
|
|
+ :props="{ label: 'nickname', key: 'id' }"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
@@ -46,7 +29,6 @@
|
|
|
<script lang="ts" setup>
|
|
|
import * as RoleApi from '@/api/system/role'
|
|
|
import * as UserApi from '@/api/system/user'
|
|
|
-import SmartTable from '@/components/SmartTable/SmartTable'
|
|
|
|
|
|
defineOptions({ name: 'SystemRoleAssignUserForm' })
|
|
|
|
|
|
@@ -61,62 +43,10 @@ const formData = reactive({
|
|
|
})
|
|
|
const formRef = ref()
|
|
|
|
|
|
-// 用户搜索条件
|
|
|
-const userSearch = reactive({
|
|
|
- nickName: '',
|
|
|
- pageNo: 1,
|
|
|
- pageSize: 10,
|
|
|
- total: 0
|
|
|
-})
|
|
|
-
|
|
|
-// 用户表格
|
|
|
-const userTableRef = ref()
|
|
|
-const userTableData = ref<any[]>([])
|
|
|
-const userColumns = [
|
|
|
- { type: 'selection', width: '50px' },
|
|
|
- { label: '工号', prop: 'employeeNo' },
|
|
|
- { label: '用户名', prop: 'nickname' },
|
|
|
- { label: '部门', prop: 'deptName' }
|
|
|
-]
|
|
|
-
|
|
|
-/** 获取用户列表 */
|
|
|
-const fetchUserList = async () => {
|
|
|
- const params: PageParam = {
|
|
|
- pageNo: userSearch.pageNo,
|
|
|
- pageSize: userSearch.pageSize,
|
|
|
- nickName: userSearch.nickName || undefined
|
|
|
- }
|
|
|
- const data = await UserApi.getUserPage(params)
|
|
|
- userTableData.value = data.list || []
|
|
|
- userSearch.total = data.total || 0
|
|
|
-
|
|
|
- // 预选已有该角色的用户
|
|
|
- await nextTick()
|
|
|
- preSelectRows()
|
|
|
-}
|
|
|
-
|
|
|
-/** 根据角色预选已有的用户 */
|
|
|
-const preSelectRows = async () => {
|
|
|
- if (!formData.roleId) return
|
|
|
- const existingUserIds = await RoleApi.getRoleUserIdList(formData.roleId)
|
|
|
- if (!existingUserIds || existingUserIds.length === 0) return
|
|
|
- const tableRef = userTableRef.value?.getTableRef?.()
|
|
|
- if (!tableRef) return
|
|
|
- userTableData.value.forEach((row: any) => {
|
|
|
- if (existingUserIds.includes(String(row.id))) {
|
|
|
- tableRef.toggleRowSelection(row, true)
|
|
|
- }
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-const onPageSizeChange = (val: number) => {
|
|
|
- userSearch.pageSize = val
|
|
|
- fetchUserList()
|
|
|
-}
|
|
|
-const onPageNoChange = (val: number) => {
|
|
|
- userSearch.pageNo = val
|
|
|
- fetchUserList()
|
|
|
-}
|
|
|
+// 所有用户列表(transfer 数据源)
|
|
|
+const userList = ref<any[]>([])
|
|
|
+// 已选中的用户 ID 列表
|
|
|
+const selectedUserIdList = ref<string[]>([])
|
|
|
|
|
|
/** 打开弹窗 */
|
|
|
const open = async (row: RoleApi.RoleVO) => {
|
|
|
@@ -127,7 +57,16 @@ const open = async (row: RoleApi.RoleVO) => {
|
|
|
formData.code = row.code
|
|
|
formLoading.value = true
|
|
|
try {
|
|
|
- await fetchUserList()
|
|
|
+ // 并行加载所有用户和已分配用户 ID
|
|
|
+ const [allUsers, existingUserIds] = await Promise.all([
|
|
|
+ UserApi.getSimpleUserList(),
|
|
|
+ RoleApi.getRoleUserIdList(row.id)
|
|
|
+ ])
|
|
|
+ userList.value = allUsers.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ nickname: `${item.nickname}${item.employeeNo ? `(${item.employeeNo})` : ''}`
|
|
|
+ }))
|
|
|
+ selectedUserIdList.value = existingUserIds || []
|
|
|
} finally {
|
|
|
formLoading.value = false
|
|
|
}
|
|
|
@@ -143,17 +82,13 @@ const submitForm = async () => {
|
|
|
}
|
|
|
formLoading.value = true
|
|
|
try {
|
|
|
- const tableRef = userTableRef.value?.getTableRef?.()
|
|
|
- // 获取所有选中的行(跨页选中)
|
|
|
- const selectedRows = tableRef?.getSelectionRows?.() || []
|
|
|
- const selectedUserIds: string[] = selectedRows.map((row: any) => String(row.id))
|
|
|
- if (selectedUserIds.length === 0) {
|
|
|
+ if (selectedUserIdList.value.length === 0) {
|
|
|
message.warning('请至少选择一个用户')
|
|
|
return
|
|
|
}
|
|
|
await RoleApi.assignRoleUsers({
|
|
|
roleId: String(formData.roleId),
|
|
|
- userIds: selectedUserIds
|
|
|
+ userIds: selectedUserIdList.value
|
|
|
})
|
|
|
message.success('分配用户成功')
|
|
|
dialogVisible.value = false
|
|
|
@@ -165,10 +100,102 @@ const submitForm = async () => {
|
|
|
|
|
|
/** 重置 */
|
|
|
const resetForm = () => {
|
|
|
- userSearch.nickName = ''
|
|
|
- userSearch.pageNo = 1
|
|
|
- userSearch.pageSize = 10
|
|
|
- userSearch.total = 0
|
|
|
- userTableData.value = []
|
|
|
+ userList.value = []
|
|
|
+ selectedUserIdList.value = []
|
|
|
}
|
|
|
</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.transfer-form-item {
|
|
|
+ margin-bottom: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.transfer-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ height: 420px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep() {
|
|
|
+ .el-transfer {
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer-panel {
|
|
|
+ flex: 1;
|
|
|
+ border: 1px solid var(--el-border-color-light);
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer-panel__header {
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
+ padding: 0 12px;
|
|
|
+ height: 40px;
|
|
|
+ line-height: 40px;
|
|
|
+
|
|
|
+ .el-checkbox__label {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer-panel__body {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .el-transfer-panel__list {
|
|
|
+ height: 100%;
|
|
|
+ padding: 4px 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer-panel__filter {
|
|
|
+ padding: 8px 12px;
|
|
|
+
|
|
|
+ .el-input__wrapper {
|
|
|
+ border-radius: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer-panel__item {
|
|
|
+ padding: 0 12px;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ background-color: var(--el-fill-color-light);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .el-transfer__buttons {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 0 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ .el-transfer__button {
|
|
|
+ width: 32px;
|
|
|
+ height: 32px;
|
|
|
+ border-radius: 50%;
|
|
|
+ padding: 0;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ &:nth-child(2) {
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|