第一次提交:Vue3 后台前端项目当前最新版
This commit is contained in:
683
src/views/admin/AdminUser.vue
Normal file
683
src/views/admin/AdminUser.vue
Normal file
@@ -0,0 +1,683 @@
|
||||
<template>
|
||||
<div class="admin-user-manager">
|
||||
<!-- 权限不足 -->
|
||||
<div v-if="!hasPermission" class="no-permission">
|
||||
<div class="no-permission-icon">🔒</div>
|
||||
<div class="no-permission-text">您无权访问此页面</div>
|
||||
<div class="no-permission-desc">请联系管理员开通权限</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="manager-container">
|
||||
<!-- 顶部操作栏 -->
|
||||
<div class="action-bar">
|
||||
<div class="filters">
|
||||
<el-input
|
||||
v-model="filters.phone"
|
||||
placeholder="用户账号"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
@keyup.enter="fetchUserList"
|
||||
/>
|
||||
<el-input
|
||||
v-model="filters.nicheng"
|
||||
placeholder="用户昵称"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
@keyup.enter="fetchUserList"
|
||||
/>
|
||||
<el-select
|
||||
v-model="filters.roleCode"
|
||||
placeholder="角色筛选"
|
||||
clearable
|
||||
style="width: 160px"
|
||||
@change="fetchUserList"
|
||||
>
|
||||
<el-option label="全部角色" value="" />
|
||||
<el-option
|
||||
v-for="role in roleList"
|
||||
:key="role.role_code"
|
||||
:label="role.role_name"
|
||||
:value="role.role_code"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="fetchUserList">搜索</el-button>
|
||||
<el-button @click="resetFilters">重置</el-button>
|
||||
</div>
|
||||
<el-button type="success" @click="openAddUserDialog">
|
||||
<el-icon><Plus /></el-icon> 添加用户
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 用户表格 -->
|
||||
<el-table :data="userList" v-loading="loading" border class="user-table">
|
||||
<el-table-column prop="phone" label="账号" width="140" />
|
||||
<el-table-column prop="nicheng" label="昵称" width="150" />
|
||||
<el-table-column label="角色" min-width="180">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-for="role in row.roles" :key="role.role_code" size="small" style="margin-right: 6px;">
|
||||
{{ role.role_name }}
|
||||
</el-tag>
|
||||
<span v-if="!row.roles.length">无角色</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" label="创建时间" width="160" />
|
||||
<el-table-column prop="update_time" label="更新时间" width="160" />
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="openDetailDialog(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
v-model:current-page="page"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="fetchUserList"
|
||||
@current-change="fetchUserList"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 用户详情弹窗 -->
|
||||
<el-dialog
|
||||
v-model="detailDialogVisible"
|
||||
:title="`编辑用户 - ${currentUser?.phone}`"
|
||||
width="700px"
|
||||
class="user-dialog"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<!-- 基本信息 -->
|
||||
<div class="info-section">
|
||||
<div class="section-title">基本信息</div>
|
||||
<el-form :model="editForm" label-width="100px">
|
||||
<el-form-item label="账号">
|
||||
<span>{{ currentUser?.phone }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称">
|
||||
<el-input v-model="editForm.nicheng" placeholder="请输入昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码">
|
||||
<el-input
|
||||
v-model="editForm.password"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="不修改请留空"
|
||||
/>
|
||||
<div class="form-hint">留空表示不修改密码,新密码至少6位</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="二级密码">
|
||||
<el-input
|
||||
v-model="editForm.erjimima"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="不修改请留空"
|
||||
/>
|
||||
<div class="form-hint">留空表示不修改二级密码,新密码至少6位</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户状态">
|
||||
<el-radio-group v-model="editForm.status">
|
||||
<el-radio :label="1">正常</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 角色管理 -->
|
||||
<div class="role-section">
|
||||
<div class="section-title">
|
||||
<span>所属角色</span>
|
||||
<el-button type="primary" size="small" @click="openAddRoleForUser">
|
||||
<el-icon><Plus /></el-icon> 添加角色
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="role-list">
|
||||
<div
|
||||
v-for="role in currentUserRoles"
|
||||
:key="role.role_code"
|
||||
class="role-tag"
|
||||
>
|
||||
{{ role.role_name }}
|
||||
<el-icon class="remove-icon" @click="removeUserRole(role)">
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-if="currentUserRoles.length === 0" class="empty-role">
|
||||
暂无角色,点击上方按钮添加
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="detailDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveUserInfo">保存修改</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 为用户添加角色的弹窗 -->
|
||||
<el-dialog
|
||||
v-model="addRoleForUserVisible"
|
||||
title="添加角色"
|
||||
width="500px"
|
||||
class="role-select-dialog"
|
||||
>
|
||||
<div class="role-select-list">
|
||||
<el-checkbox-group v-model="selectedRoleCodes">
|
||||
<div
|
||||
v-for="role in availableRolesForUser"
|
||||
:key="role.role_code"
|
||||
class="role-select-item"
|
||||
>
|
||||
<el-checkbox :label="role.role_code">
|
||||
<span>{{ role.role_name }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="addRoleForUserVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddUserRoles">确认添加</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加用户弹窗 -->
|
||||
<el-dialog
|
||||
v-model="addUserDialogVisible"
|
||||
title="添加后台用户"
|
||||
width="600px"
|
||||
class="user-dialog"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<el-form :model="newUserForm" label-width="100px" ref="addUserFormRef">
|
||||
<el-form-item label="账号" required>
|
||||
<el-input v-model="newUserForm.phone" placeholder="手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" required>
|
||||
<el-input
|
||||
v-model="newUserForm.password"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="至少6位"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="二级密码" required>
|
||||
<el-input
|
||||
v-model="newUserForm.erjimima"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="至少6位"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="昵称" required>
|
||||
<el-input v-model="newUserForm.nicheng" placeholder="用户昵称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="选择角色">
|
||||
<div class="role-select-list">
|
||||
<el-checkbox-group v-model="newUserRoleCodes">
|
||||
<div
|
||||
v-for="role in roleList"
|
||||
:key="role.role_code"
|
||||
class="role-select-item"
|
||||
>
|
||||
<el-checkbox :label="role.role_code">
|
||||
<span>{{ role.role_name }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="addUserDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddUser">确认添加</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, Close } from '@element-plus/icons-vue'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const username = localStorage.getItem('username') || ''
|
||||
|
||||
// 权限状态
|
||||
const hasPermission = ref(true)
|
||||
|
||||
// 角色列表(用于筛选和添加)
|
||||
const roleList = ref([])
|
||||
|
||||
// 用户列表数据
|
||||
const userList = ref([])
|
||||
const loading = ref(false)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const total = ref(0)
|
||||
|
||||
// 筛选条件
|
||||
const filters = ref({
|
||||
phone: '',
|
||||
nicheng: '',
|
||||
roleCode: ''
|
||||
})
|
||||
|
||||
// 当前用户详情相关
|
||||
const detailDialogVisible = ref(false)
|
||||
const currentUser = ref(null)
|
||||
const editForm = ref({
|
||||
nicheng: '',
|
||||
password: '',
|
||||
erjimima: '',
|
||||
status: 1
|
||||
})
|
||||
const currentUserRoles = ref([])
|
||||
|
||||
// 为用户添加角色弹窗
|
||||
const addRoleForUserVisible = ref(false)
|
||||
const selectedRoleCodes = ref([])
|
||||
const availableRolesForUser = computed(() => {
|
||||
const existingCodes = currentUserRoles.value.map(r => r.role_code)
|
||||
return roleList.value.filter(r => !existingCodes.includes(r.role_code))
|
||||
})
|
||||
|
||||
// 添加用户弹窗
|
||||
const addUserDialogVisible = ref(false)
|
||||
const newUserForm = ref({
|
||||
phone: '',
|
||||
password: '',
|
||||
erjimima: '',
|
||||
nicheng: ''
|
||||
})
|
||||
const newUserRoleCodes = ref([])
|
||||
|
||||
// ==================== 接口调用 ====================
|
||||
// 获取角色列表(第一个接口)
|
||||
const fetchRoleList = async () => {
|
||||
try {
|
||||
const res = await request.post('/houtai/hqyhjsgl', { username })
|
||||
if (res.code === 0) {
|
||||
roleList.value = res.data.roles || []
|
||||
return true
|
||||
} else if (res.code === 403) {
|
||||
hasPermission.value = false
|
||||
return false
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取角色列表失败')
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户列表
|
||||
const fetchUserList = async () => {
|
||||
if (!hasPermission.value) return
|
||||
loading.value = true
|
||||
try {
|
||||
const params = {
|
||||
username,
|
||||
page: page.value,
|
||||
pageSize: pageSize.value,
|
||||
...filters.value
|
||||
}
|
||||
// 清理空值
|
||||
Object.keys(params).forEach(k => {
|
||||
if (params[k] === '' || params[k] === null || params[k] === undefined) {
|
||||
delete params[k]
|
||||
}
|
||||
})
|
||||
const res = await request.post('/houtai/hqjsyhsj', params)
|
||||
if (res.code === 0) {
|
||||
userList.value = res.data.list || []
|
||||
total.value = res.data.total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取用户列表失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置筛选
|
||||
const resetFilters = () => {
|
||||
filters.value = { phone: '', nicheng: '', roleCode: '' }
|
||||
page.value = 1
|
||||
fetchUserList()
|
||||
}
|
||||
|
||||
// 打开用户详情弹窗
|
||||
const openDetailDialog = (user) => {
|
||||
currentUser.value = user
|
||||
editForm.value = {
|
||||
nicheng: user.nicheng || '',
|
||||
password: '',
|
||||
erjimima: '',
|
||||
status: user.status
|
||||
}
|
||||
currentUserRoles.value = [...(user.roles || [])]
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 保存用户修改(昵称、密码、二级密码、状态、角色已通过其他接口单独处理)
|
||||
const saveUserInfo = async () => {
|
||||
// 构建修改参数
|
||||
const payload = {
|
||||
username,
|
||||
action: 'update_user_info',
|
||||
phone: currentUser.value.phone,
|
||||
nicheng: editForm.value.nicheng,
|
||||
status: editForm.value.status
|
||||
}
|
||||
if (editForm.value.password && editForm.value.password.length >= 6) {
|
||||
payload.password = editForm.value.password
|
||||
}
|
||||
if (editForm.value.erjimima && editForm.value.erjimima.length >= 6) {
|
||||
payload.erjimima = editForm.value.erjimima
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/xghtyhsj', payload)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('保存成功')
|
||||
detailDialogVisible.value = false
|
||||
fetchUserList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 为用户添加角色(打开弹窗)
|
||||
const openAddRoleForUser = () => {
|
||||
selectedRoleCodes.value = []
|
||||
addRoleForUserVisible.value = true
|
||||
}
|
||||
|
||||
// 确认添加角色
|
||||
const confirmAddUserRoles = async () => {
|
||||
if (selectedRoleCodes.value.length === 0) {
|
||||
ElMessage.warning('请至少选择一个角色')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/xghtyhsj', {
|
||||
username,
|
||||
action: 'add_user_roles',
|
||||
phone: currentUser.value.phone,
|
||||
role_codes: selectedRoleCodes.value
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('角色添加成功')
|
||||
// 刷新当前用户的角色列表
|
||||
const updatedUser = userList.value.find(u => u.phone === currentUser.value.phone)
|
||||
if (updatedUser) {
|
||||
currentUserRoles.value = [...(updatedUser.roles || []), ...roleList.value.filter(r => selectedRoleCodes.value.includes(r.role_code))]
|
||||
}
|
||||
addRoleForUserVisible.value = false
|
||||
fetchUserList() // 刷新列表
|
||||
} else {
|
||||
ElMessage.error(res.msg || '添加失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 移除用户的某个角色
|
||||
const removeUserRole = async (role) => {
|
||||
try {
|
||||
const res = await request.post('/houtai/xghtyhsj', {
|
||||
username,
|
||||
action: 'remove_user_role',
|
||||
phone: currentUser.value.phone,
|
||||
role_code: role.role_code
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('角色移除成功')
|
||||
const idx = currentUserRoles.value.findIndex(r => r.role_code === role.role_code)
|
||||
if (idx !== -1) currentUserRoles.value.splice(idx, 1)
|
||||
fetchUserList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '移除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 添加用户弹窗
|
||||
const openAddUserDialog = () => {
|
||||
newUserForm.value = { phone: '', password: '', erjimima: '', nicheng: '' }
|
||||
newUserRoleCodes.value = []
|
||||
addUserDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认添加用户
|
||||
const confirmAddUser = async () => {
|
||||
if (!newUserForm.value.phone || !newUserForm.value.password || !newUserForm.value.erjimima || !newUserForm.value.nicheng) {
|
||||
ElMessage.warning('请填写完整信息')
|
||||
return
|
||||
}
|
||||
if (newUserForm.value.password.length < 6 || newUserForm.value.erjimima.length < 6) {
|
||||
ElMessage.warning('密码和二级密码至少6位')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/tjyhjs', {
|
||||
username,
|
||||
phone: newUserForm.value.phone,
|
||||
password: newUserForm.value.password,
|
||||
erjimima: newUserForm.value.erjimima,
|
||||
nicheng: newUserForm.value.nicheng,
|
||||
role_codes: newUserRoleCodes.value
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('添加成功')
|
||||
addUserDialogVisible.value = false
|
||||
fetchUserList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '添加失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onMounted(async () => {
|
||||
if (!username) {
|
||||
ElMessage.error('未获取到用户信息')
|
||||
return
|
||||
}
|
||||
const success = await fetchRoleList()
|
||||
if (success) {
|
||||
await fetchUserList()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-user-manager {
|
||||
background: radial-gradient(circle at 20% 30%, #0a0f1a, #03060c);
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
color: #eef2ff;
|
||||
}
|
||||
.no-permission {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60vh;
|
||||
}
|
||||
.no-permission-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.no-permission-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.no-permission-desc {
|
||||
font-size: 14px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
.manager-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* ========== 表格样式修正:强制深色背景 + 白色文字 ========== */
|
||||
.user-table {
|
||||
background: rgba(6,12,20,0.6);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.el-table) {
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
:deep(.el-table th) {
|
||||
background: rgba(0,242,255,0.15);
|
||||
color: #00f2ff;
|
||||
font-weight: 500;
|
||||
border-bottom: 1px solid rgba(0,242,255,0.3);
|
||||
}
|
||||
:deep(.el-table tr) {
|
||||
background: transparent;
|
||||
}
|
||||
:deep(.el-table td) {
|
||||
background: transparent;
|
||||
color: #fff;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.08);
|
||||
}
|
||||
/* 斑马纹行使用半透明浅色背景,保证文字可见 */
|
||||
:deep(.el-table--striped .el-table__row--striped td) {
|
||||
background: rgba(255,255,255,0.06);
|
||||
}
|
||||
/* 悬浮行高亮 */
|
||||
:deep(.el-table__row:hover td) {
|
||||
background: rgba(0,242,255,0.1) !important;
|
||||
}
|
||||
/* 空数据提示文字颜色 */
|
||||
:deep(.el-table__empty-text) {
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
:deep(.el-dialog) {
|
||||
background: rgba(6,12,20,0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(0,242,255,0.3);
|
||||
border-radius: 20px;
|
||||
}
|
||||
:deep(.el-dialog__title) {
|
||||
color: #00f2ff;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
:deep(.el-input__wrapper) {
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-color: rgba(0,242,255,0.3);
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
color: #fff;
|
||||
}
|
||||
:deep(.el-input__inner::placeholder) {
|
||||
color: rgba(255,255,255,0.4);
|
||||
}
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00f2ff;
|
||||
margin: 20px 0 16px;
|
||||
border-left: 4px solid #00f2ff;
|
||||
padding-left: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.role-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
.role-tag {
|
||||
background: rgba(0,242,255,0.1);
|
||||
border: 1px solid rgba(0,242,255,0.3);
|
||||
border-radius: 20px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
.remove-icon {
|
||||
cursor: pointer;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
.empty-role {
|
||||
color: rgba(255,255,255,0.4);
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.role-select-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.role-select-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.form-hint {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
||||
8
src/views/admin/AdminUserDetail.vue
Normal file
8
src/views/admin/AdminUserDetail.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
651
src/views/admin/Role.vue
Normal file
651
src/views/admin/Role.vue
Normal file
@@ -0,0 +1,651 @@
|
||||
<template>
|
||||
<div class="role-manager">
|
||||
<!-- 权限不足 -->
|
||||
<div v-if="!hasPermission" class="no-permission">
|
||||
<div class="no-permission-icon">🔒</div>
|
||||
<div class="no-permission-text">您无权访问此页面</div>
|
||||
<div class="no-permission-desc">请联系管理员开通权限</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="manager-container">
|
||||
<!-- 顶部操作栏 -->
|
||||
<div class="action-bar">
|
||||
<div class="stats-tip">
|
||||
<el-tag type="info" size="large">共 {{ roleList.length }} 个角色</el-tag>
|
||||
</div>
|
||||
<el-button type="primary" @click="openAddRoleDialog">
|
||||
<el-icon><Plus /></el-icon> 添加角色
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 角色卡片网格 -->
|
||||
<div class="role-grid">
|
||||
<div v-for="role in roleList" :key="role.role_code" class="role-card">
|
||||
<div class="card-header">
|
||||
<div class="role-name">{{ role.role_name }}</div>
|
||||
<!-- 不展示角色编码 -->
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="desc">{{ role.description || '暂无描述' }}</div>
|
||||
<div class="user-count">
|
||||
<el-icon><User /></el-icon> 关联用户:{{ role.user_count || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<el-button type="primary" link @click="openDetailDialog(role)">
|
||||
<el-icon><View /></el-icon> 详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="role.role_code !== '000001'"
|
||||
type="danger"
|
||||
link
|
||||
@click="deleteRole(role)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon> 删除
|
||||
</el-button>
|
||||
<el-tag v-else type="warning" size="small">超级管理员</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 角色详情弹窗 -->
|
||||
<el-dialog
|
||||
v-model="detailDialogVisible"
|
||||
:title="`编辑角色 - ${currentRole?.role_name}`"
|
||||
width="800px"
|
||||
class="role-dialog"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<!-- 基本信息编辑 -->
|
||||
<div class="info-section">
|
||||
<div class="section-title">基本信息</div>
|
||||
<el-form :model="editForm" label-width="80px">
|
||||
<el-form-item label="角色名称">
|
||||
<el-input
|
||||
v-model="editForm.role_name"
|
||||
:disabled="currentRole?.role_code === '000001'"
|
||||
placeholder="请输入角色名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述">
|
||||
<el-input
|
||||
v-model="editForm.description"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:disabled="currentRole?.role_code === '000001'"
|
||||
placeholder="请输入角色描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 权限管理 -->
|
||||
<div class="permission-section">
|
||||
<div class="section-title">
|
||||
<span>已绑定权限</span>
|
||||
<el-button type="primary" size="small" @click="openAddPermissionDialog">
|
||||
<el-icon><Plus /></el-icon> 添加权限
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="permission-list">
|
||||
<div
|
||||
v-for="perm in currentRolePermissions"
|
||||
:key="perm.perm_code"
|
||||
class="permission-tag"
|
||||
>
|
||||
<span>{{ perm.perm_name }}</span>
|
||||
<!-- 超级管理员不能移除 000001 权限 -->
|
||||
<el-icon
|
||||
v-if="!(currentRole?.role_code === '000001' && perm.perm_code === '000001')"
|
||||
class="remove-icon"
|
||||
@click="removePermission(perm)"
|
||||
>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-if="currentRolePermissions.length === 0" class="empty-perm">
|
||||
暂无权限,点击上方按钮添加
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="detailDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveRoleInfo">保存修改</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加权限弹窗 -->
|
||||
<el-dialog
|
||||
v-model="addPermDialogVisible"
|
||||
title="添加权限"
|
||||
width="500px"
|
||||
class="perm-select-dialog"
|
||||
>
|
||||
<div class="perm-select-list">
|
||||
<el-checkbox-group v-model="selectedPermCodes">
|
||||
<div
|
||||
v-for="perm in availablePermissions"
|
||||
:key="perm.perm_code"
|
||||
class="perm-select-item"
|
||||
>
|
||||
<el-checkbox :label="perm.perm_code">
|
||||
<div class="perm-info">
|
||||
<span class="perm-name">{{ perm.perm_name }}</span>
|
||||
<span class="perm-desc">{{ perm.description }}</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="addPermDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddPermissions">确认添加</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加角色弹窗 -->
|
||||
<el-dialog
|
||||
v-model="addRoleDialogVisible"
|
||||
title="添加角色"
|
||||
width="700px"
|
||||
class="role-dialog"
|
||||
destroy-on-close
|
||||
>
|
||||
<div class="dialog-content">
|
||||
<el-form :model="newRoleForm" label-width="80px">
|
||||
<el-form-item label="角色名称" required>
|
||||
<el-input v-model="newRoleForm.role_name" placeholder="如:运营专员" />
|
||||
</el-form-item>
|
||||
<el-form-item label="角色描述">
|
||||
<el-input
|
||||
v-model="newRoleForm.description"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="角色描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择权限">
|
||||
<div class="perm-select-list">
|
||||
<el-checkbox-group v-model="newRolePermCodes">
|
||||
<div
|
||||
v-for="perm in allPermissions"
|
||||
:key="perm.perm_code"
|
||||
class="perm-select-item"
|
||||
>
|
||||
<el-checkbox :label="perm.perm_code">
|
||||
<div class="perm-info">
|
||||
<span class="perm-name">{{ perm.perm_name }}</span>
|
||||
<span class="perm-desc">{{ perm.description }}</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="addRoleDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmAddRole">确认添加</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, User, View, Delete, Close } from '@element-plus/icons-vue'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const hasPermission = ref(true)
|
||||
|
||||
// 数据
|
||||
const roleList = ref([])
|
||||
const allPermissions = ref([])
|
||||
|
||||
// 当前角色详情
|
||||
const detailDialogVisible = ref(false)
|
||||
const currentRole = ref(null)
|
||||
const editForm = ref({ role_name: '', description: '' })
|
||||
const currentRolePermissions = ref([])
|
||||
|
||||
// 添加权限弹窗
|
||||
const addPermDialogVisible = ref(false)
|
||||
const selectedPermCodes = ref([])
|
||||
const availablePermissions = computed(() => {
|
||||
// 过滤掉已经拥有的权限
|
||||
const boundCodes = currentRolePermissions.value.map(p => p.perm_code)
|
||||
return allPermissions.value.filter(p => !boundCodes.includes(p.perm_code))
|
||||
})
|
||||
|
||||
// 添加角色弹窗
|
||||
const addRoleDialogVisible = ref(false)
|
||||
const newRoleForm = ref({ role_name: '', description: '' })
|
||||
const newRolePermCodes = ref([])
|
||||
|
||||
const username = localStorage.getItem('username') || ''
|
||||
|
||||
// 获取角色和权限列表
|
||||
const fetchRoleList = async () => {
|
||||
try {
|
||||
const res = await request.post('/houtai/gljshq', { username })
|
||||
if (res.code === 0) {
|
||||
roleList.value = res.data.roles || []
|
||||
allPermissions.value = res.data.permissions || []
|
||||
} else if (res.code === 403) {
|
||||
hasPermission.value = false
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 保存角色信息(基本信息和权限)
|
||||
const saveRoleInfo = async () => {
|
||||
if (!currentRole.value) return
|
||||
// 超级管理员不能修改名称和描述(前端已禁用输入框,但后端也会校验)
|
||||
try {
|
||||
const permCodes = currentRolePermissions.value.map(p => p.perm_code)
|
||||
const res = await request.post('/houtai/sctjjsnr', {
|
||||
username,
|
||||
action: 'update_role',
|
||||
role_code: currentRole.value.role_code,
|
||||
role_name: editForm.value.role_name,
|
||||
description: editForm.value.description,
|
||||
perm_codes: permCodes
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('保存成功')
|
||||
detailDialogVisible.value = false
|
||||
await fetchRoleList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除角色
|
||||
const deleteRole = (role) => {
|
||||
ElMessageBox.confirm(`确定要删除角色“${role.role_name}”吗?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
try {
|
||||
const res = await request.post('/houtai/sctjjsnr', {
|
||||
username,
|
||||
action: 'delete_role',
|
||||
role_code: role.role_code
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('删除成功')
|
||||
await fetchRoleList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '删除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 打开详情弹窗
|
||||
const openDetailDialog = (role) => {
|
||||
currentRole.value = role
|
||||
editForm.value = {
|
||||
role_name: role.role_name,
|
||||
description: role.description || ''
|
||||
}
|
||||
currentRolePermissions.value = [...(role.permissions || [])]
|
||||
detailDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 打开添加权限弹窗
|
||||
const openAddPermissionDialog = () => {
|
||||
selectedPermCodes.value = []
|
||||
addPermDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认添加权限
|
||||
const confirmAddPermissions = async () => {
|
||||
if (selectedPermCodes.value.length === 0) {
|
||||
ElMessage.warning('请至少选择一个权限')
|
||||
return
|
||||
}
|
||||
// 逐个添加(后端接口支持批量,但为了简单,循环调用或一次性传数组?我们按后端接口一次传一个)
|
||||
// 这里简化:调用添加权限接口多次(或要求后端支持批量,但当前接口是单个)
|
||||
// 为了效率,可以改为后端支持批量,但按现有设计,我们依次调用
|
||||
try {
|
||||
for (const permCode of selectedPermCodes.value) {
|
||||
await request.post('/houtai/sctjjsnr', {
|
||||
username,
|
||||
action: 'add_permission',
|
||||
role_code: currentRole.value.role_code,
|
||||
perm_code: permCode
|
||||
})
|
||||
}
|
||||
ElMessage.success('权限添加成功')
|
||||
// 刷新当前角色权限列表
|
||||
const res = await request.post('/houtai/gljshq', { username })
|
||||
if (res.code === 0) {
|
||||
const updatedRole = res.data.roles.find(r => r.role_code === currentRole.value.role_code)
|
||||
if (updatedRole) {
|
||||
currentRolePermissions.value = updatedRole.permissions || []
|
||||
}
|
||||
await fetchRoleList()
|
||||
}
|
||||
addPermDialogVisible.value = false
|
||||
} catch (error) {
|
||||
ElMessage.error('添加权限失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 移除权限
|
||||
const removePermission = async (perm) => {
|
||||
try {
|
||||
const res = await request.post('/houtai/sctjjsnr', {
|
||||
username,
|
||||
action: 'remove_permission',
|
||||
role_code: currentRole.value.role_code,
|
||||
perm_code: perm.perm_code
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('移除成功')
|
||||
// 更新本地列表
|
||||
const idx = currentRolePermissions.value.findIndex(p => p.perm_code === perm.perm_code)
|
||||
if (idx !== -1) currentRolePermissions.value.splice(idx, 1)
|
||||
} else {
|
||||
ElMessage.error(res.msg || '移除失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
// 添加角色
|
||||
const confirmAddRole = async () => {
|
||||
if (!newRoleForm.value.role_name) {
|
||||
ElMessage.warning('请填写角色名称')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await request.post('/houtai/tjjs', {
|
||||
username,
|
||||
role_name: newRoleForm.value.role_name,
|
||||
description: newRoleForm.value.description,
|
||||
perm_codes: newRolePermCodes.value
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('添加成功')
|
||||
addRoleDialogVisible.value = false
|
||||
newRoleForm.value = { role_name: '', description: '' }
|
||||
newRolePermCodes.value = []
|
||||
await fetchRoleList()
|
||||
} else {
|
||||
ElMessage.error(res.msg || '添加失败')
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('网络错误')
|
||||
}
|
||||
}
|
||||
|
||||
const openAddRoleDialog = () => {
|
||||
newRoleForm.value = { role_name: '', description: '' }
|
||||
newRolePermCodes.value = []
|
||||
addRoleDialogVisible.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!username) {
|
||||
ElMessage.error('未获取到用户信息')
|
||||
return
|
||||
}
|
||||
fetchRoleList()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* ========= 机甲风样式 ========= */
|
||||
.role-manager {
|
||||
background: radial-gradient(circle at 20% 30%, #0a0f1a, #03060c);
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
color: #eef2ff;
|
||||
}
|
||||
|
||||
.no-permission {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 60vh;
|
||||
}
|
||||
.no-permission-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.no-permission-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.no-permission-desc {
|
||||
font-size: 14px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
}
|
||||
|
||||
.manager-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 操作栏 */
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.stats-tip :deep(.el-tag) {
|
||||
background: rgba(0,242,255,0.1);
|
||||
border-color: #00f2ff;
|
||||
color: #00f2ff;
|
||||
font-size: 16px;
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
/* 卡片网格 */
|
||||
.role-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||||
gap: 24px;
|
||||
}
|
||||
.role-card {
|
||||
background: rgba(6,12,20,0.6);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(0,242,255,0.2);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.role-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: #00f2ff;
|
||||
box-shadow: 0 8px 24px rgba(0,242,255,0.2);
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0,242,255,0.3);
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.role-name {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: #00f2ff;
|
||||
}
|
||||
.role-code {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.5);
|
||||
font-family: monospace;
|
||||
}
|
||||
.card-body {
|
||||
margin: 16px 0;
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
color: rgba(255,255,255,0.8);
|
||||
line-height: 1.4;
|
||||
margin-bottom: 12px;
|
||||
min-height: 40px;
|
||||
}
|
||||
.user-count {
|
||||
font-size: 13px;
|
||||
color: rgba(0,242,255,0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
border-top: 1px solid rgba(255,255,255,0.1);
|
||||
padding-top: 12px;
|
||||
}
|
||||
.card-actions .el-button {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
:deep(.el-dialog) {
|
||||
background: rgba(6,12,20,0.95);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(0,242,255,0.3);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 0 30px rgba(0,242,255,0.2);
|
||||
}
|
||||
:deep(.el-dialog__title) {
|
||||
color: #00f2ff;
|
||||
font-weight: bold;
|
||||
}
|
||||
:deep(.el-dialog__headerbtn .el-dialog__close) {
|
||||
color: #00f2ff;
|
||||
}
|
||||
:deep(.el-form-item__label) {
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
:deep(.el-input__wrapper) {
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-color: rgba(0,242,255,0.3);
|
||||
box-shadow: none;
|
||||
}
|
||||
:deep(.el-input__inner) {
|
||||
color: #fff;
|
||||
}
|
||||
:deep(.el-textarea__inner) {
|
||||
background: rgba(0,0,0,0.5);
|
||||
border-color: rgba(0,242,255,0.3);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dialog-content {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00f2ff;
|
||||
margin: 20px 0 16px 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-left: 4px solid #00f2ff;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.section-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.permission-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.permission-tag {
|
||||
background: rgba(0,242,255,0.1);
|
||||
border: 1px solid rgba(0,242,255,0.3);
|
||||
border-radius: 20px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #fff;
|
||||
}
|
||||
.permission-tag .remove-icon {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #ff4d4f;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.permission-tag .remove-icon:hover {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.empty-perm {
|
||||
color: rgba(255,255,255,0.4);
|
||||
font-size: 14px;
|
||||
padding: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 权限选择列表 */
|
||||
.perm-select-list {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
.perm-select-item {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.perm-select-item :deep(.el-checkbox__label) {
|
||||
width: 100%;
|
||||
}
|
||||
.perm-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.perm-name {
|
||||
font-weight: bold;
|
||||
color: #00f2ff;
|
||||
}
|
||||
.perm-desc {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.6);
|
||||
}
|
||||
|
||||
/* 按钮图标间距 */
|
||||
.el-button .el-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
8
src/views/admin/RoleDetail.vue
Normal file
8
src/views/admin/RoleDetail.vue
Normal file
@@ -0,0 +1,8 @@
|
||||
<template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
Reference in New Issue
Block a user