diff --git a/app/Services/Auth/DepartmentService.php b/app/Services/Auth/DepartmentService.php
index d0153c0..75c648b 100644
--- a/app/Services/Auth/DepartmentService.php
+++ b/app/Services/Auth/DepartmentService.php
@@ -324,4 +324,40 @@ class DepartmentService
}
return $this->isDescendant($id, $child->parent_id);
}
+
+ /**
+ * 获取部门及所有子部门的ID列表
+ *
+ * @param int $departmentId 部门ID
+ * @param array $departments 所有部门数据
+ * @return array
+ */
+ public function getDepartmentAndChildrenIds(int $departmentId, array $departments = null): array
+ {
+ if ($departments === null) {
+ $departments = Department::where('status', 1)->get()->keyBy('id')->toArray();
+ }
+
+ $ids = [$departmentId];
+ $this->collectChildrenIds($departmentId, $departments, $ids);
+
+ return $ids;
+ }
+
+ /**
+ * 递归收集子部门ID
+ *
+ * @param int $parentId 父部门ID
+ * @param array $departments 所有部门数据
+ * @param array &$ids ID收集数组
+ */
+ private function collectChildrenIds(int $parentId, array $departments, array &$ids): void
+ {
+ foreach ($departments as $department) {
+ if ($department['parent_id'] == $parentId) {
+ $ids[] = $department['id'];
+ $this->collectChildrenIds($department['id'], $departments, $ids);
+ }
+ }
+ }
}
diff --git a/app/Services/Auth/UserService.php b/app/Services/Auth/UserService.php
index ae44bd1..a1335b2 100644
--- a/app/Services/Auth/UserService.php
+++ b/app/Services/Auth/UserService.php
@@ -3,6 +3,7 @@
namespace App\Services\Auth;
use App\Models\Auth\User;
+use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
@@ -14,6 +15,20 @@ use App\Jobs\Auth\UserExportJob;
class UserService
{
+ protected $departmentService;
+
+ public function __construct(DepartmentService $departmentService)
+ {
+ $this->departmentService = $departmentService;
+ }
+
+ /**
+ * 获取当前登录用户ID
+ */
+ protected function getCurrentUserId(): int
+ {
+ return Auth::guard('admin')->id();
+ }
/**
* 获取用户列表
*/
@@ -32,7 +47,9 @@ class UserService
}
if (!empty($params['department_id'])) {
- $query->where('department_id', $params['department_id']);
+ // 获取部门及所有子部门的ID
+ $departmentIds = $this->departmentService->getDepartmentAndChildrenIds($params['department_id']);
+ $query->whereIn('department_id', $departmentIds);
}
if (isset($params['status']) && $params['status'] !== '') {
@@ -226,6 +243,15 @@ class UserService
*/
public function batchDelete(array $ids): int
{
+ $currentUserId = $this->getCurrentUserId();
+
+ // 检查是否包含当前用户
+ if (in_array($currentUserId, $ids)) {
+ throw ValidationException::withMessages([
+ 'ids' => ['不能删除当前登录用户'],
+ ]);
+ }
+
return User::whereIn('id', $ids)->delete();
}
@@ -234,6 +260,15 @@ class UserService
*/
public function batchUpdateStatus(array $ids, int $status): int
{
+ $currentUserId = $this->getCurrentUserId();
+
+ // 如果是禁用操作,检查是否包含当前用户
+ if ($status === 0 && in_array($currentUserId, $ids)) {
+ throw ValidationException::withMessages([
+ 'ids' => ['不能禁用当前登录用户'],
+ ]);
+ }
+
return User::whereIn('id', $ids)->update(['status' => $status]);
}
diff --git a/resources/admin/src/pages/auth/roles/components/PermissionDialog.vue b/resources/admin/src/pages/auth/roles/components/PermissionDialog.vue
index 4c58c75..221b1ad 100644
--- a/resources/admin/src/pages/auth/roles/components/PermissionDialog.vue
+++ b/resources/admin/src/pages/auth/roles/components/PermissionDialog.vue
@@ -5,8 +5,8 @@
-
- {{ name }}
+
+ {{ title }}
@@ -37,7 +37,7 @@ const checkedPermissionIds = ref([])
// 树字段映射
const fieldNames = {
- title: 'name',
+ title: 'title',
key: 'id',
children: 'children'
}
diff --git a/resources/admin/src/pages/auth/users/components/BatchRoleDialog.vue b/resources/admin/src/pages/auth/users/components/BatchRoleDialog.vue
new file mode 100644
index 0000000..3e229f1
--- /dev/null
+++ b/resources/admin/src/pages/auth/users/components/BatchRoleDialog.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/admin/src/pages/auth/users/components/DepartmentDialog.vue b/resources/admin/src/pages/auth/users/components/DepartmentDialog.vue
new file mode 100644
index 0000000..f3ea1a2
--- /dev/null
+++ b/resources/admin/src/pages/auth/users/components/DepartmentDialog.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/admin/src/pages/auth/users/components/RoleDialog.vue b/resources/admin/src/pages/auth/users/components/RoleDialog.vue
index fc4f332..b0ffb33 100644
--- a/resources/admin/src/pages/auth/users/components/RoleDialog.vue
+++ b/resources/admin/src/pages/auth/users/components/RoleDialog.vue
@@ -1,18 +1,23 @@
-
-
-
-
-
- {{ role.name }}
-
-
-
-
-
- 取 消
- 保 存
-
+
+
+
+
+
+
+
+
+
@@ -21,104 +26,100 @@ import { ref, reactive } from 'vue'
import { message } from 'ant-design-vue'
import authApi from '@/api/auth'
-const emit = defineEmits(['success', 'closed'])
-
const visible = ref(false)
-const isSaveing = ref(false)
+const loading = ref(false)
+const roleLoading = ref(false)
+const roleOptions = ref([])
+const selectedRoleIds = ref([])
+const userId = ref(null)
-// 表单数据
-const form = reactive({
- id: '',
- role_ids: []
+const userForm = reactive({
+ username: ''
})
-// 选中的角色
-const checkedRoles = ref([])
-
-// 角色列表
-const roleList = ref([])
-
-// 打开对话框
+// 打开弹窗
const open = () => {
visible.value = true
- return {
- open,
- setData,
- close
- }
+ loadRoles()
}
-// 关闭对话框
-const close = () => {
- visible.value = false
+// 设置用户数据
+const setData = (user) => {
+ userId.value = user.id
+ userForm.username = user.username
+ // 设置已选择的角色
+ selectedRoleIds.value = (user.roles || []).map(role => role.id)
}
-// 处理取消
-const handleCancel = () => {
- emit('closed')
- visible.value = false
-}
-
-// 提交保存
-const submit = async () => {
+// 加载角色列表
+const loadRoles = async () => {
try {
- isSaveing.value = true
-
- // 获取选中的角色 ID
- form.role_ids = checkedRoles.value || []
-
- const res = await authApi.users.batchRoles.post({
- ids: [form.id],
- role_ids: form.role_ids
- })
-
- isSaveing.value = false
+ roleLoading.value = true
+ const res = await authApi.roles.list.get({ page_size: 1000 })
if (res.code === 200) {
- emit('success', form)
- visible.value = false
- message.success('操作成功')
- } else {
- message.error(res.message || '操作失败')
+ roleOptions.value = (res.data.list || []).map(role => ({
+ id: role.id,
+ name: role.name,
+ code: role.code
+ }))
}
} catch (error) {
- console.error('保存角色失败:', error)
- isSaveing.value = false
- message.error('操作失败')
+ console.error('加载角色列表失败:', error)
+ } finally {
+ roleLoading.value = false
}
}
-// 获取角色列表
-const getRoles = async () => {
+// 角色过滤
+const filterOption = (input, option) => {
+ const name = option?.name?.toLowerCase() || ''
+ const code = option?.code?.toLowerCase() || ''
+ const keyword = input?.toLowerCase() || ''
+ return name.includes(keyword) || code.includes(keyword)
+}
+
+// 确认
+const handleOk = async () => {
+ if (!userId.value) {
+ message.warning('用户ID不能为空')
+ return
+ }
+
try {
- const res = await authApi.roles.all.get()
- roleList.value = res.data || []
+ loading.value = true
+ const res = await authApi.users.batchRoles.post({
+ ids: [userId.value],
+ role_ids: selectedRoleIds.value
+ })
+
+ if (res.code === 200) {
+ message.success('设置成功')
+ emit('success')
+ handleCancel()
+ } else {
+ message.error(res.message || '设置失败')
+ }
} catch (error) {
- console.error('获取角色列表失败:', error)
- message.error('获取角色列表失败')
+ console.error('设置角色失败:', error)
+ message.error(error.message || '设置失败')
+ } finally {
+ loading.value = false
}
}
-// 设置数据
-const setData = (data) => {
- form.id = data.id
- checkedRoles.value = data.roles ? data.roles.map(item => item.id) : []
+// 取消
+const handleCancel = () => {
+ visible.value = false
+ loading.value = false
+ userId.value = null
+ userForm.username = ''
+ selectedRoleIds.value = []
}
-// 组件挂载时加载数据
-getRoles()
+const emit = defineEmits(['success'])
-// 暴露方法给父组件
defineExpose({
open,
- setData,
- close
+ setData
})
-
-
diff --git a/resources/admin/src/pages/auth/users/index.vue b/resources/admin/src/pages/auth/users/index.vue
index 4e354b3..abef509 100644
--- a/resources/admin/src/pages/auth/users/index.vue
+++ b/resources/admin/src/pages/auth/users/index.vue
@@ -10,7 +10,7 @@
+ :field-names="{ title: 'name', key: 'id', children: 'children' }" show-line default-expand-all @select="onDeptSelect">
@@ -127,6 +127,12 @@
+
+
+
+
+
+
@@ -159,6 +165,8 @@ import scImport from '@/components/scImport/index.vue'
import scExport from '@/components/scExport/index.vue'
import saveDialog from './components/SaveDialog.vue'
import roleDialog from './components/RoleDialog.vue'
+import departmentDialog from './components/DepartmentDialog.vue'
+import batchRoleDialog from './components/BatchRoleDialog.vue'
import authApi from '@/api/auth'
import { useTable } from '@/hooks/useTable'
@@ -200,6 +208,8 @@ const {
const dialog = reactive({
save: false,
role: false,
+ department: false,
+ batchRole: false,
import: false,
export: false
})
@@ -207,6 +217,8 @@ const dialog = reactive({
// 弹窗引用
const saveDialogRef = ref(null)
const roleDialogRef = ref(null)
+const departmentDialogRef = ref(null)
+const batchRoleDialogRef = ref(null)
// 部门树数据
const departmentTree = ref([])
@@ -366,8 +378,10 @@ const handleBatchDepartment = () => {
message.warning('请选择要分配部门的用户')
return
}
- // TODO: 实现批量分配部门弹窗
- message.info('批量分配部门功能开发中...')
+ dialog.department = true
+ setTimeout(() => {
+ departmentDialogRef.value?.open(selectedRows.value.map(item => item.id))
+ }, 0)
}
// 批量分配角色
@@ -376,8 +390,10 @@ const handleBatchRoles = () => {
message.warning('请选择要分配角色的用户')
return
}
- // TODO: 实现批量分配角色弹窗
- message.info('批量分配角色功能开发中...')
+ dialog.batchRole = true
+ setTimeout(() => {
+ batchRoleDialogRef.value?.open(selectedRows.value.map(item => item.id))
+ }, 0)
}
// 导出数据
@@ -472,7 +488,10 @@ const handleEdit = (record) => {
const handleRole = (record) => {
dialog.role = true
setTimeout(() => {
- roleDialogRef.value?.open().setData(record)
+ if (roleDialogRef.value) {
+ roleDialogRef.value.open()
+ roleDialogRef.value.setData(record)
+ }
}, 0)
}
@@ -502,6 +521,18 @@ const handleRoleSuccess = () => {
refreshTable()
}
+// 批量分配部门成功回调
+const handleDepartmentSuccess = () => {
+ selectedRows.value = []
+ refreshTable()
+}
+
+// 批量分配角色成功回调
+const handleBatchRoleSuccess = () => {
+ selectedRows.value = []
+ refreshTable()
+}
+
// 初始化
onMounted(() => {
loadDepartmentTree()