chore: sync local latest state and repository cleanup
This commit is contained in:
297
frontend/admin/src/views/PermissionUsersView.vue
Normal file
297
frontend/admin/src/views/PermissionUsersView.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<section class="space-y-6">
|
||||
<header>
|
||||
<h1 class="mos-title text-2xl font-semibold">用户权限管理</h1>
|
||||
<p class="mos-muted mt-2 text-sm">管理用户权限、数据范围和角色分配。</p>
|
||||
</header>
|
||||
|
||||
<div class="mos-card p-4">
|
||||
<div class="flex flex-wrap gap-4 mb-4">
|
||||
<input v-model="searchQuery" class="mos-input w-64" placeholder="搜索用户姓名或ID..." />
|
||||
<select v-model="filterRole" class="mos-input w-40">
|
||||
<option value="">全部角色</option>
|
||||
<option v-for="role in roles" :key="role.code" :value="role.code">
|
||||
{{ role.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
<tr class="border-b border-mosquito-line text-left text-sm text-mosquito-ink/70">
|
||||
<th class="pb-3 font-medium">用户</th>
|
||||
<th class="pb-3 font-medium">角色</th>
|
||||
<th class="pb-3 font-medium">数据范围</th>
|
||||
<th class="pb-3 font-medium">部门</th>
|
||||
<th class="pb-3 font-medium">状态</th>
|
||||
<th class="pb-3 font-medium">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="user in filteredUsers" :key="user.id" class="border-b border-mosquito-line/50">
|
||||
<td class="py-3">
|
||||
<div class="text-sm font-medium text-mosquito-ink">{{ user.realName || user.username }}</div>
|
||||
<div class="text-xs text-mosquito-ink/60">{{ user.username }} (ID: {{ user.id }})</div>
|
||||
</td>
|
||||
<td class="py-3 text-sm text-mosquito-ink">{{ user.roleName }}</td>
|
||||
<td class="py-3 text-sm text-mosquito-ink">
|
||||
<span :class="dataScopeClass(user.dataScope)" class="rounded-full px-2 py-1 text-xs font-semibold">
|
||||
{{ user.dataScopeName }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-3 text-sm text-mosquito-ink">{{ user.departmentName || '-' }}</td>
|
||||
<td class="py-3">
|
||||
<span
|
||||
:class="user.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-700'"
|
||||
class="rounded-full px-2 py-1 text-xs font-semibold"
|
||||
>
|
||||
{{ user.status === 'active' ? '正常' : '禁用' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<div class="flex gap-2">
|
||||
<PermissionButton permission="user.index.update.ALL" variant="secondary" @click="editUser(user)">
|
||||
<span class="text-sm text-mosquito-accent hover:underline">编辑</span>
|
||||
</PermissionButton>
|
||||
<PermissionButton permission="role.index.manage.ALL" variant="secondary" @click="assignRole(user)">
|
||||
<span class="text-sm text-mosquito-accent hover:underline">分配角色</span>
|
||||
</PermissionButton>
|
||||
<PermissionButton permission="user.index.delete.ALL" variant="danger" @click="removeUser(user)">
|
||||
<span class="text-sm text-rose-600 hover:underline">移除</span>
|
||||
</PermissionButton>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div v-if="!filteredUsers.length" class="py-8 text-center text-mosquito-ink/60">
|
||||
暂无用户数据
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分配角色弹窗 -->
|
||||
<div v-if="showRoleModal" class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div class="mos-card p-6 w-full max-w-md">
|
||||
<h3 class="text-lg font-semibold mb-4">分配角色 - {{ selectedUser?.username }}</h3>
|
||||
<div class="space-y-3">
|
||||
<label v-for="role in roles" :key="role.code" class="flex items-center gap-3 p-3 rounded-lg border border-mosquito-line cursor-pointer hover:bg-mosquito-bg/50">
|
||||
<input type="radio" v-model="selectedRoleId" :value="role.id" class="h-4 w-4" />
|
||||
<div>
|
||||
<div class="text-sm font-medium text-mosquito-ink">{{ role.name }}</div>
|
||||
<div class="text-xs text-mosquito-ink/60">{{ role.description }}</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex gap-3 mt-6">
|
||||
<button class="mos-btn mos-btn-primary flex-1" @click="confirmAssignRole">确认</button>
|
||||
<button class="mos-btn mos-btn-secondary flex-1" @click="showRoleModal = false">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { userService } from '@/services/userManage'
|
||||
import { permissionService } from '@/services/permission'
|
||||
import PermissionButton from '../components/PermissionButton.vue'
|
||||
|
||||
const showMessage = (msg: string, type: 'success' | 'error' = 'success') => {
|
||||
if (type === 'error') {
|
||||
alert(msg)
|
||||
} else {
|
||||
console.log(msg)
|
||||
}
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: number
|
||||
username: string
|
||||
realName?: string
|
||||
roleCode: string
|
||||
roleName: string
|
||||
dataScope: string
|
||||
dataScopeName: string
|
||||
departmentId?: number
|
||||
departmentName?: string
|
||||
status: string
|
||||
}
|
||||
|
||||
interface Role {
|
||||
id: number
|
||||
code: string
|
||||
name: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
const searchQuery = ref('')
|
||||
const filterRole = ref('')
|
||||
const showRoleModal = ref(false)
|
||||
const selectedUser = ref<User | null>(null)
|
||||
const selectedRoleId = ref<number | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const roles = ref<Role[]>([])
|
||||
const users = ref<User[]>([])
|
||||
const totalUsers = ref(0)
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
|
||||
// 角色code到ID的映射
|
||||
const roleCodeToId = ref<Record<string, number>>({})
|
||||
|
||||
// 加载角色列表
|
||||
const loadRoles = async () => {
|
||||
try {
|
||||
const roleList = await permissionService.getRoles()
|
||||
roles.value = roleList.map((r: any) => ({
|
||||
id: r.id,
|
||||
code: r.roleCode,
|
||||
name: r.roleName,
|
||||
description: r.description
|
||||
}))
|
||||
// 建立映射
|
||||
roles.value.forEach(role => {
|
||||
roleCodeToId.value[role.code] = role.id
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.error('加载角色列表失败:', error)
|
||||
showMessage(error.message || '加载角色列表失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// 加载用户列表
|
||||
const loadUsers = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const result = await userService.getUsers({
|
||||
page: currentPage.value,
|
||||
size: pageSize.value,
|
||||
keyword: searchQuery.value || undefined
|
||||
})
|
||||
// 获取每个用户的角色
|
||||
const userList: User[] = []
|
||||
for (const user of result.items) {
|
||||
try {
|
||||
const roleCodes = await userService.getUserRoles(user.id)
|
||||
const roleCode = roleCodes[0] || ''
|
||||
const roleInfo = roles.value.find(r => r.code === roleCode)
|
||||
userList.push({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
realName: user.realName,
|
||||
roleCode: roleCode,
|
||||
roleName: roleInfo?.name || roleCode || '未分配',
|
||||
dataScope: 'ALL', // 默认值
|
||||
dataScopeName: '全部数据',
|
||||
departmentId: user.departmentId,
|
||||
departmentName: user.departmentName,
|
||||
status: user.status?.toLowerCase() || 'active'
|
||||
})
|
||||
} catch (e) {
|
||||
// 如果获取用户角色失败,使用默认信息
|
||||
userList.push({
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
realName: user.realName,
|
||||
roleCode: '',
|
||||
roleName: '未分配',
|
||||
dataScope: 'ALL',
|
||||
dataScopeName: '全部数据',
|
||||
departmentId: user.departmentId,
|
||||
departmentName: user.departmentName,
|
||||
status: user.status?.toLowerCase() || 'active'
|
||||
})
|
||||
}
|
||||
}
|
||||
users.value = userList
|
||||
totalUsers.value = result.total
|
||||
} catch (error: any) {
|
||||
console.error('加载用户列表失败:', error)
|
||||
showMessage(error.message || '加载用户列表失败', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const filteredUsers = computed(() => {
|
||||
return users.value.filter((user) => {
|
||||
const matchesSearch = !searchQuery.value ||
|
||||
user.username.includes(searchQuery.value) ||
|
||||
user.realName?.includes(searchQuery.value) ||
|
||||
String(user.id).includes(searchQuery.value)
|
||||
const matchesRole = !filterRole.value || user.roleCode === filterRole.value
|
||||
return matchesSearch && matchesRole
|
||||
})
|
||||
})
|
||||
|
||||
const dataScopeClass = (scope: string) => {
|
||||
switch (scope) {
|
||||
case 'ALL':
|
||||
return 'bg-purple-100 text-purple-700'
|
||||
case 'DEPARTMENT':
|
||||
return 'bg-blue-100 text-blue-700'
|
||||
case 'OWN':
|
||||
return 'bg-gray-100 text-gray-700'
|
||||
default:
|
||||
return 'bg-gray-100 text-gray-700'
|
||||
}
|
||||
}
|
||||
|
||||
const editUser = (user: User) => {
|
||||
alert(`编辑用户: ${user.username}`)
|
||||
}
|
||||
|
||||
const assignRole = (user: User) => {
|
||||
selectedUser.value = user
|
||||
selectedRoleId.value = roleCodeToId.value[user.roleCode] || null
|
||||
showRoleModal.value = true
|
||||
}
|
||||
|
||||
const confirmAssignRole = async () => {
|
||||
if (selectedUser.value && selectedRoleId.value) {
|
||||
try {
|
||||
loading.value = true
|
||||
await userService.assignRoles(selectedUser.value.id, [selectedRoleId.value], '管理员分配角色')
|
||||
showMessage('角色分配成功,请等待审批')
|
||||
await loadUsers()
|
||||
} catch (error: any) {
|
||||
showMessage(error.message || '分配角色失败', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
showRoleModal.value = false
|
||||
}
|
||||
|
||||
const removeUser = async (user: User) => {
|
||||
if (confirm(`确定移除用户"${user.username}"吗?`)) {
|
||||
try {
|
||||
loading.value = true
|
||||
// 使用紧急模式删除
|
||||
await userService.deleteUser(user.id)
|
||||
showMessage('用户删除成功')
|
||||
await loadUsers()
|
||||
} catch (error: any) {
|
||||
showMessage(error.message || '删除用户失败', 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索用户
|
||||
const handleSearch = () => {
|
||||
currentPage.value = 1
|
||||
loadUsers()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadRoles()
|
||||
await loadUsers()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user