系统管理完善

This commit is contained in:
Lxq
2025-12-23 14:02:53 +08:00
parent 2e9ec4b50f
commit f661d432b4
15 changed files with 819 additions and 10 deletions

View File

@@ -0,0 +1,39 @@
import request from '@/utils/request'
export function islandInfoadd(data: any) {
return request({
url: '/islandInfo/add',
method: 'post',
data,
})
}
export function islandInfodel(id: string | number) {
return request({
url: `/islandInfo/del/${id}`,
method: 'delete',
})
}
export function islandInfoupd(data: any) {
return request({
url: '/islandInfo/update',
method: 'put',
data,
})
}
export function islandInfolist(data: any) {
return request({
url: '/islandInfo/listPage',
method: 'get',
params: data,
})
}
export function islandInfobyid(id: string | number) {
return request({
url: `/islandInfo/getById/${id}`,
method: 'get',
})
}

View File

@@ -37,3 +37,11 @@ export function rolebyid(id: string | number) {
method: 'get',
})
}
export function rleselect(data: any) {
return request({
url: '/role/list',
method: 'get',
params: data,
})
}

View File

@@ -0,0 +1,67 @@
import request from '@/utils/request'
// 新增用户角色关联
export function userRoleAdd(data: any) {
return request({
url: '/user-role/add',
method: 'post',
data,
})
}
// 根据ID删除用户角色关联
export function userRoleDel(id: string | number) {
return request({
url: `/user-role/del/${id}`,
method: 'delete',
})
}
// 分页查询用户角色关联列表
export function userRoleList(data: any) {
return request({
url: '/user-role/listPage',
method: 'get',
params: data,
})
}
// 根据ID查询用户角色关联
export function userRoleById(id: string | number) {
return request({
url: `/user-role/getById/${id}`,
method: 'get',
})
}
// 根据角色ID查询关联用户先封装不调用
export function userRoleByRoleId(roleId: string | number) {
return request({
url: `/user-role/getByRoleId/${roleId}`,
method: 'get',
})
}
// 根据角色ID删除所有关联先封装不调用
export function userRoleDelByRoleId(roleId: string | number) {
return request({
url: `/user-role/delByRoleId/${roleId}`,
method: 'delete',
})
}
// 根据用户ID查询关联角色先封装不调用
export function userRoleByUserId(userId: string | number) {
return request({
url: `/user-role/getByUserId/${userId}`,
method: 'get',
})
}
// 根据用户ID删除所有关联先封装不调用
export function userRoleDelByUserId(userId: string | number) {
return request({
url: `/user-role/delByUserId/${userId}`,
method: 'delete',
})
}

View File

@@ -32,6 +32,11 @@ const router = createRouter({
name: 'manage-log',
component: () => import('../views/manage-log/index.vue'),
},
{
path: '/user-role',
name: 'user-role',
component: () => import('../views/user-role/index.vue'),
},
],
},
],

View File

@@ -56,6 +56,10 @@
<el-icon><Document /></el-icon>
<span>操作日志管理</span>
</el-menu-item>
<el-menu-item index="/user-role">
<el-icon><UserFilled /></el-icon>
<span>用户角色管理</span>
</el-menu-item>
</el-sub-menu>
</el-menu>
</el-aside>
@@ -72,7 +76,7 @@
import { ref, computed } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { User, Setting, Avatar, OfficeBuilding, Briefcase, Document, CaretBottom } from '@element-plus/icons-vue'
import { User, Setting, Avatar, OfficeBuilding, Briefcase, Document, CaretBottom, UserFilled } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
const router = useRouter()

View File

@@ -205,8 +205,8 @@ import {
departmentadd,
departmentdel,
departtree,
} from '@/api/department'
import { userselect } from '@/api/user'
} from '@/api/system/department'
import { userselect } from '@/api/system/user'
interface DeptItem {
id?: number | string

View File

@@ -152,7 +152,7 @@ import {
managelogdel,
manageloglist,
managelogupd,
} from '@/api/manage-log'
} from '@/api/system/manage-log'
interface LogItem {
id?: string | number

View File

@@ -127,7 +127,7 @@
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { positionadd, positionbyid, positiondel, positionlist, positionupd } from '@/api/position'
import { positionadd, positionbyid, positiondel, positionlist, positionupd } from '@/api/system/position'
interface PositionItem {
id?: number | string

View File

@@ -111,7 +111,7 @@
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { roleadd, rolebyid, roledel, rolelist, roleupd } from '@/api/role'
import { roleadd, rolebyid, roledel, rolelist, roleupd } from '@/api/system/role'
interface RoleItem {
id?: number | string

View File

@@ -0,0 +1,527 @@
<template>
<div class="user-role-page">
<el-card class="search-card" shadow="never">
<div class="search-bar">
<el-form :inline="true" :model="queryForm" label-width="80px">
<el-form-item label="用户">
<el-select
v-model="queryForm.userId"
filterable
clearable
placeholder="请选择用户"
style="width: 200px"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.userName || item.nicke || item.nickName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="角色">
<el-select
v-model="queryForm.roleId"
filterable
clearable
placeholder="请选择角色"
style="width: 200px"
>
<el-option
v-for="item in roleOptions"
:key="item.id"
:label="item.roleName || item.roleCode"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
<div class="toolbar">
<el-button type="primary" @click="openDrawer('create')">新增用户角色关联</el-button>
</div>
</div>
</el-card>
<el-card shadow="never">
<el-table
:data="tableData"
stripe
border
style="width: 100%"
v-loading="loading"
>
<el-table-column type="index" label="序号" width="70" />
<el-table-column
prop="userId"
label="用户名"
min-width="150"
>
<template #default="scope">
{{ getUserName(scope.row.userId) }}
</template>
</el-table-column>
<el-table-column
prop="roleId"
label="角色名"
min-width="150"
>
<template #default="scope">
{{ getRoleName(scope.row.roleId) }}
</template>
</el-table-column>
<el-table-column
prop="remark"
label="备注"
min-width="200"
:formatter="formatCell"
/>
<el-table-column label="操作" width="120" fixed="right">
<template #default="scope">
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination">
<el-pagination
v-model:current-page="pagination.pageNum"
v-model:page-size="pagination.pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</el-card>
<el-drawer
v-model="drawerVisible"
title="新增用户角色关联"
direction="rtl"
:size="500"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="100px"
class="drawer-form"
>
<el-form-item label="用户" prop="userId">
<el-select
v-model="form.userId"
filterable
clearable
placeholder="请选择用户"
style="width: 100%"
>
<el-option
v-for="item in userOptions"
:key="item.id"
:label="item.userName || item.nicke || item.nickName"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="角色" prop="roleId">
<el-select
v-model="form.roleId"
filterable
clearable
placeholder="请选择角色"
style="width: 100%"
>
<el-option
v-for="item in roleOptions"
:key="item.id"
:label="item.roleName || item.roleCode"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注"
:rows="3"
maxlength="200"
show-word-limit
/>
</el-form-item>
</el-form>
<template #footer>
<div class="drawer-footer">
<el-button @click="drawerVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="submitForm">
保存
</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { userRoleAdd, userRoleDel, userRoleList, userRoleByUserId } from '@/api/system/user-role'
import { userselect } from '@/api/system/user'
import { rleselect } from '@/api/system/role'
interface UserRoleItem {
id?: number | string
userId: number | string
roleId: number | string
remark?: string
}
interface UserItem {
id: number | string
userName?: string
nicke?: string
nickName?: string
}
interface RoleItem {
id: number | string
roleName?: string
roleCode?: string
}
const loading = ref(false)
const submitLoading = ref(false)
const tableData = ref<UserRoleItem[]>([])
const total = ref(0)
const userOptions = ref<UserItem[]>([])
const roleOptions = ref<RoleItem[]>([])
const userMap = ref<Map<number | string, string>>(new Map())
const roleMap = ref<Map<number | string, string>>(new Map())
const queryForm = reactive({
userId: null as number | string | null,
roleId: null as number | string | null,
})
const pagination = reactive({
pageNum: 1,
pageSize: 10,
})
const drawerVisible = ref(false)
const formRef = ref<FormInstance>()
const form = reactive<UserRoleItem>({
userId: '',
roleId: '',
remark: '',
})
const rules: FormRules = {
userId: [{ required: true, message: '请选择用户', trigger: 'change' }],
roleId: [{ required: true, message: '请选择角色', trigger: 'change' }],
}
const resetForm = () => {
form.userId = ''
form.roleId = ''
form.remark = ''
formRef.value?.clearValidate()
}
const formatCell = (_row: any, _column: any, value: any) => {
if (value === 0) return 0
return value === undefined || value === null || value === '' ? '暂无' : value
}
// 获取用户名
const getUserName = (userId: number | string | null | undefined) => {
if (!userId) return '暂无'
return userMap.value.get(userId) || '暂无'
}
// 获取角色名
const getRoleName = (roleId: number | string | null | undefined) => {
if (!roleId) return '暂无'
return roleMap.value.get(roleId) || '暂无'
}
// 加载用户列表
const loadUsers = async () => {
try {
const res: any = await userselect({})
const data = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
userOptions.value = data
userMap.value.clear()
data.forEach((user: UserItem) => {
if (user.id) {
const userName = user.userName || user.nicke || user.nickName || ''
userMap.value.set(user.id, userName)
}
})
} catch (error) {
console.error('load user list error', error)
ElMessage.error('加载用户列表失败')
userOptions.value = []
}
}
// 加载角色列表
const loadRoles = async () => {
try {
const res: any = await rleselect({})
const data = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
roleOptions.value = data
roleMap.value.clear()
data.forEach((role: RoleItem) => {
if (role.id) {
const roleName = role.roleName || role.roleCode || ''
roleMap.value.set(role.id, roleName)
}
})
} catch (error) {
console.error('load role list error', error)
ElMessage.error('加载角色列表失败')
roleOptions.value = []
}
}
// 加载用户角色关联列表
const loadList = async () => {
loading.value = true
try {
// 确保用户和角色列表已加载
if (userOptions.value.length === 0) {
await loadUsers()
}
if (roleOptions.value.length === 0) {
await loadRoles()
}
const params: any = {
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
}
if (queryForm.userId) {
params.userId = queryForm.userId
}
if (queryForm.roleId) {
params.roleId = queryForm.roleId
}
const res: any = await userRoleList(params)
console.log('用户角色关联列表API返回数据:', res)
let pageData = res?.data
if (!pageData && (res?.records || res?.total !== undefined)) {
pageData = res
}
const recordsFromData =
pageData?.records ||
pageData?.list ||
pageData?.rows ||
pageData?.items ||
(Array.isArray(pageData?.data) ? pageData.data : undefined)
const records = Array.isArray(recordsFromData)
? recordsFromData
: Array.isArray(pageData)
? pageData
: Array.isArray(res?.data)
? res.data
: []
const totalValue =
pageData?.total ??
pageData?.count ??
pageData?.totalCount ??
res?.total ??
(Array.isArray(records) ? records.length : 0)
tableData.value = records
total.value = Number(totalValue) || 0
console.log('解析后的数据:', {
records: records.length,
total: total.value,
firstRecord: records[0],
})
} catch (error: any) {
console.error('load user role list error', error)
const errorMsg = error?.message || error?.msg || '加载用户角色关联列表失败'
ElMessage.error(errorMsg)
tableData.value = []
total.value = 0
} finally {
loading.value = false
}
}
const handleSearch = () => {
pagination.pageNum = 1
loadList()
}
const resetSearch = () => {
queryForm.userId = null
queryForm.roleId = null
handleSearch()
}
const handleSizeChange = (size: number) => {
pagination.pageSize = size
loadList()
}
const handleCurrentChange = (page: number) => {
pagination.pageNum = page
loadList()
}
const openDrawer = async (mode: 'create') => {
resetForm()
drawerVisible.value = true
// 加载用户和角色数据
await Promise.all([loadUsers(), loadRoles()])
}
const submitForm = () => {
formRef.value?.validate(async (valid) => {
if (!valid) return
// 检查用户是否已拥有该角色
try {
const res: any = await userRoleByUserId(form.userId)
const data = res?.data ?? res ?? {}
// 兼容多种返回格式
const userRoles = Array.isArray(data)
? data
: Array.isArray(data.data)
? data.data
: Array.isArray(data.records)
? data.records
: Array.isArray(data.list)
? data.list
: []
// 检查是否已存在该角色关联
const existingRole = userRoles.find((item: any) => item.roleId === form.roleId)
if (existingRole) {
// 获取用户名和角色名
const userName = getUserName(form.userId)
const roleName = getRoleName(form.roleId)
ElMessage.warning(`用户【${userName}】已拥有【${roleName}】角色,无需重复分配`)
return
}
} catch (error: any) {
console.error('check user role error', error)
// 如果检查失败,继续执行新增操作,让后端处理重复情况
}
submitLoading.value = true
try {
const payload: any = {
userId: form.userId,
roleId: form.roleId,
remark: form.remark || '',
}
await userRoleAdd(payload)
ElMessage.success('新增成功')
drawerVisible.value = false
loadList()
} catch (error: any) {
console.error('submit user role error', error)
const errorMsg = error?.message || error?.msg || '新增失败'
ElMessage.error(errorMsg)
} finally {
submitLoading.value = false
}
})
}
const handleDelete = (row: UserRoleItem) => {
if (!row.id) {
ElMessage.warning('缺少关联ID')
return
}
const userName = getUserName(row.userId)
const roleName = getRoleName(row.roleId)
ElMessageBox.confirm(`确认删除用户「${userName}」与角色「${roleName}」的关联吗?`, '提示', {
type: 'warning',
})
.then(async () => {
if (row.id == null) {
ElMessage.warning('缺少关联ID无法删除')
return
}
try {
await userRoleDel(row.id)
ElMessage.success('删除成功')
loadList()
} catch (error: any) {
console.error('delete user role error', error)
const errorMsg = error?.message || error?.msg || '删除失败'
ElMessage.error(errorMsg)
}
})
.catch(() => {})
}
onMounted(() => {
loadUsers()
loadRoles()
loadList()
})
</script>
<style scoped>
.user-role-page {
display: flex;
flex-direction: column;
gap: 12px;
}
.search-card {
padding-bottom: 4px;
}
.search-bar {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.toolbar {
display: flex;
align-items: center;
gap: 8px;
}
.pagination {
margin-top: 16px;
display: flex;
justify-content: flex-end;
}
.drawer-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
}
</style>

View File

@@ -100,9 +100,10 @@
min-width="150"
:formatter="formatCell"
/>
<el-table-column label="操作" width="180" fixed="right">
<el-table-column label="操作" width="250" fixed="right">
<template #default="scope">
<el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
<el-button type="success" link @click="openRoleDialog(scope.row)">分配角色</el-button>
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
@@ -213,6 +214,31 @@
</div>
</template>
</el-drawer>
<!-- 分配角色弹框 -->
<el-dialog
v-model="roleDialogVisible"
title="分配角色"
width="500px"
>
<el-checkbox-group v-model="selectedRoleIds">
<el-checkbox
v-for="role in roleOptions"
:key="role.id"
:label="role.id"
>
{{ role.roleName || role.roleCode }}
</el-checkbox>
</el-checkbox-group>
<template #footer>
<div class="dialog-footer">
<el-button @click="roleDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="assignRoleLoading" @click="handleAssignRole">
保存
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
@@ -220,9 +246,11 @@
import { onMounted, reactive, ref, computed } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useradd, userbyid, userdel, userlist, userupd } from '@/api/user'
import { departtree } from '@/api/department'
import { positionselect } from '@/api/position'
import { useradd, userbyid, userdel, userlist, userupd } from '@/api/system/user'
import { departtree } from '@/api/system/department'
import { positionselect } from '@/api/system/position'
import { userRoleAdd, userRoleByUserId, userRoleDel } from '@/api/system/user-role'
import { rleselect } from '@/api/system/role'
interface UserItem {
id?: number | string
@@ -253,6 +281,12 @@ interface PositionItem {
posiCode?: string
}
interface RoleItem {
id?: number | string
roleName?: string
roleCode?: string
}
const loading = ref(false)
const submitLoading = ref(false)
const tableData = ref<UserItem[]>([])
@@ -261,6 +295,12 @@ const deptTreeData = ref<DeptItem[]>([])
const positionOptions = ref<PositionItem[]>([])
const deptMap = ref<Map<number | string, string>>(new Map())
const posMap = ref<Map<number | string, string>>(new Map())
const roleDialogVisible = ref(false)
const assignRoleLoading = ref(false)
const roleOptions = ref<RoleItem[]>([])
const selectedRoleIds = ref<(number | string)[]>([])
const currentAssignUserId = ref<number | string | null>(null)
const originalUserRoles = ref<any[]>([]) // 保存用户原有的角色关联列表
const queryForm = reactive({
userName: '',
@@ -638,9 +678,128 @@ const handleDelete = (row: UserItem) => {
.catch(() => {})
}
// 加载角色列表
const loadRoleList = async () => {
try {
const res: any = await rleselect({})
const data = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
roleOptions.value = data
} catch (error) {
console.error('load role list error', error)
ElMessage.error('加载角色列表失败')
roleOptions.value = []
}
}
// 打开角色分配弹框
const openRoleDialog = async (row: UserItem) => {
if (!row.id) {
ElMessage.warning('缺少用户ID')
return
}
currentAssignUserId.value = row.id
selectedRoleIds.value = []
roleDialogVisible.value = true
// 确保角色列表已加载
if (roleOptions.value.length === 0) {
await loadRoleList()
}
// 获取用户已有的角色
try {
const res: any = await userRoleByUserId(row.id)
const data = res?.data ?? res ?? {}
// 兼容多种返回格式
const userRoles = Array.isArray(data)
? data
: Array.isArray(data.data)
? data.data
: Array.isArray(data.records)
? data.records
: Array.isArray(data.list)
? data.list
: []
// 保存原有的角色关联列表包含ID
originalUserRoles.value = userRoles
// 提取已有角色的ID列表
if (Array.isArray(userRoles) && userRoles.length > 0) {
selectedRoleIds.value = userRoles
.map((item: any) => item.roleId)
.filter((id: any) => id !== undefined && id !== null)
} else {
selectedRoleIds.value = []
originalUserRoles.value = []
}
} catch (error) {
console.error('load user roles error', error)
// 如果获取失败,不影响弹框打开,只是不预选角色
originalUserRoles.value = []
}
}
// 分配角色
const handleAssignRole = async () => {
if (!currentAssignUserId.value) {
ElMessage.warning('缺少用户ID')
return
}
assignRoleLoading.value = true
try {
// 获取原有的角色ID列表
const originalRoleIds = originalUserRoles.value
.map((item: any) => item.roleId)
.filter((id: any) => id !== undefined && id !== null)
// 计算需要新增的角色(新选中但原来没有的)
const rolesToAdd = selectedRoleIds.value.filter(
(roleId) => !originalRoleIds.includes(roleId)
)
// 计算需要删除的角色(原来有但现在未选中的)
const rolesToDelete = originalUserRoles.value.filter(
(item: any) => !selectedRoleIds.value.includes(item.roleId)
)
// 执行新增操作
const addPromises = rolesToAdd.map((roleId) => {
return userRoleAdd({
userId: currentAssignUserId.value,
roleId: roleId,
remark: '',
})
})
// 执行删除操作
const deletePromises = rolesToDelete.map((item: any) => {
if (item.id) {
return userRoleDel(item.id)
}
return Promise.resolve()
})
// 等待所有操作完成
await Promise.all([...addPromises, ...deletePromises])
ElMessage.success('分配角色成功')
roleDialogVisible.value = false
} catch (error: any) {
console.error('assign role error', error)
const errorMsg = error?.message || error?.msg || '分配角色失败'
ElMessage.error(errorMsg)
} finally {
assignRoleLoading.value = false
}
}
onMounted(() => {
loadDeptTree()
loadPositionList()
loadRoleList()
loadList()
})
</script>