Files
wenzi/frontend/admin/src/views/PermissionUsersView.vue

298 lines
9.9 KiB
Vue
Raw Normal View History

<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>