Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -1 +1 @@
|
|||||||
VITE_API_URL=http://192.168.110.204:9090
|
VITE_API_URL=http://223.71.122.54:9090
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
VITE_API_URL=http://192.168.110.204:9090
|
VITE_API_URL=http://223.71.122.54:9090
|
||||||
|
|||||||
39
rc_autoplc_front/src/api/business/islandinfo.ts
Normal file
39
rc_autoplc_front/src/api/business/islandinfo.ts
Normal 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',
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ export function positionupd(data: any) {
|
|||||||
|
|
||||||
export function positionlist(data: any) {
|
export function positionlist(data: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/position/listPpage',
|
url: '/position/listPage',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: data,
|
params: data,
|
||||||
})
|
})
|
||||||
@@ -36,4 +36,12 @@ export function rolebyid(id: string | number) {
|
|||||||
url: `/role/getById/${id}`,
|
url: `/role/getById/${id}`,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rleselect(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/role/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
67
rc_autoplc_front/src/api/system/user-role.ts
Normal file
67
rc_autoplc_front/src/api/system/user-role.ts
Normal 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',
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -31,6 +31,14 @@ export function userlist(data: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function userselect(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/user/list',
|
||||||
|
method: 'get',
|
||||||
|
params: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function userbyid(id: string | number) {
|
export function userbyid(id: string | number) {
|
||||||
return request({
|
return request({
|
||||||
url: `/user/getById/${id}`,
|
url: `/user/getById/${id}`,
|
||||||
@@ -5,28 +5,39 @@ const router = createRouter({
|
|||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'user',
|
component: () => import('../views/Layout.vue'),
|
||||||
component: () => import('../views/user/index.vue'),
|
children: [
|
||||||
},
|
{
|
||||||
{
|
path: '/user',
|
||||||
path: '/role',
|
name: 'user',
|
||||||
name: 'role',
|
component: () => import('../views/user/index.vue'),
|
||||||
component: () => import('../views/role/index.vue'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/role',
|
||||||
path: '/department',
|
name: 'role',
|
||||||
name: 'department',
|
component: () => import('../views/role/index.vue'),
|
||||||
component: () => import('../views/department/index.vue'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/department',
|
||||||
path: '/position',
|
name: 'department',
|
||||||
name: 'position',
|
component: () => import('../views/department/index.vue'),
|
||||||
component: () => import('../views/position/index.vue'),
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '/position',
|
||||||
path: '/manage-log',
|
name: 'position',
|
||||||
name: 'manage-log',
|
component: () => import('../views/position/index.vue'),
|
||||||
component: () => import('../views/manage-log/index.vue'),
|
},
|
||||||
|
{
|
||||||
|
path: '/manage-log',
|
||||||
|
name: 'manage-log',
|
||||||
|
component: () => import('../views/manage-log/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/user-role',
|
||||||
|
name: 'user-role',
|
||||||
|
component: () => import('../views/user-role/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
195
rc_autoplc_front/src/views/Layout.vue
Normal file
195
rc_autoplc_front/src/views/Layout.vue
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<div class="layout-container">
|
||||||
|
<!-- 顶部导航栏 -->
|
||||||
|
<el-header class="layout-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<h1 class="project-title">北京融创智能仪器管理系统</h1>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<el-dropdown @command="handleCommand">
|
||||||
|
<span class="user-info">
|
||||||
|
<el-icon><User /></el-icon>
|
||||||
|
<span class="username">{{ userInfo.username || '管理员' }}</span>
|
||||||
|
<el-icon class="el-icon--right"><CaretBottom /></el-icon>
|
||||||
|
</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="logout">退出登录</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
|
||||||
|
<!-- 主体区域 -->
|
||||||
|
<el-container class="layout-body">
|
||||||
|
<!-- 左侧菜单 -->
|
||||||
|
<el-aside width="200px" class="layout-aside">
|
||||||
|
<el-menu
|
||||||
|
:default-active="activeMenu"
|
||||||
|
class="sidebar-menu"
|
||||||
|
router
|
||||||
|
:collapse="false"
|
||||||
|
>
|
||||||
|
<el-sub-menu index="system">
|
||||||
|
<template #title>
|
||||||
|
<el-icon><Setting /></el-icon>
|
||||||
|
<span>系统管理</span>
|
||||||
|
</template>
|
||||||
|
<el-menu-item index="/user">
|
||||||
|
<el-icon><User /></el-icon>
|
||||||
|
<span>用户管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/role">
|
||||||
|
<el-icon><Avatar /></el-icon>
|
||||||
|
<span>角色管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/department">
|
||||||
|
<el-icon><OfficeBuilding /></el-icon>
|
||||||
|
<span>部门管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/position">
|
||||||
|
<el-icon><Briefcase /></el-icon>
|
||||||
|
<span>职位管理</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="/manage-log">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- 右侧内容区 -->
|
||||||
|
<el-main class="layout-main">
|
||||||
|
<RouterView />
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
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, UserFilled } from '@element-plus/icons-vue'
|
||||||
|
import { useAuthStore } from '@/stores/auth'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
// 用户信息(可以从store或localStorage获取)
|
||||||
|
const userInfo = ref({
|
||||||
|
username: localStorage.getItem('username') || '管理员',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 当前激活的菜单
|
||||||
|
const activeMenu = computed(() => {
|
||||||
|
return route.path
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理下拉菜单命令
|
||||||
|
const handleCommand = (command: string) => {
|
||||||
|
if (command === 'logout') {
|
||||||
|
ElMessageBox.confirm('确认退出登录吗?', '提示', {
|
||||||
|
type: 'warning',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// 清除token和用户信息
|
||||||
|
authStore.removeToken()
|
||||||
|
localStorage.removeItem('username')
|
||||||
|
ElMessage.success('退出成功')
|
||||||
|
// 跳转到登录页(如果有登录页)
|
||||||
|
// router.push('/login')
|
||||||
|
})
|
||||||
|
.catch(() => {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.layout-container {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-header {
|
||||||
|
height: 60px;
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.project-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #fff;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info .el-icon {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-body {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-aside {
|
||||||
|
background-color: #fff;
|
||||||
|
border-right: 1px solid #e6e6e6;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-menu {
|
||||||
|
border-right: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-main {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@@ -88,10 +88,10 @@
|
|||||||
:formatter="formatCell"
|
:formatter="formatCell"
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="leaderName"
|
prop="leaderId"
|
||||||
label="部门领导"
|
label="部门领导"
|
||||||
min-width="150"
|
min-width="150"
|
||||||
:formatter="formatCell"
|
:formatter="formatLeaderCell"
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="remark"
|
prop="remark"
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
:data="treeOptions"
|
:data="treeOptions"
|
||||||
:props="{ value: 'id', label: 'deptName', children: 'children' }"
|
:props="{ value: 'id', label: 'deptName', children: 'children' }"
|
||||||
check-strictly
|
check-strictly
|
||||||
placeholder="请选择上级部门(可选)"
|
placeholder="请选择上级部门"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
@@ -160,13 +160,13 @@
|
|||||||
v-model="form.leaderId"
|
v-model="form.leaderId"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择部门领导(可选)"
|
placeholder="请选择部门领导"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in userOptions"
|
v-for="item in userOptions"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.realName || item.nickName || item.username || item.name"
|
:label="item.nicke || item.realName || item.nickName || item.username || item.name || ''"
|
||||||
:value="item.id"
|
:value="item.id"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -205,7 +205,8 @@ import {
|
|||||||
departmentadd,
|
departmentadd,
|
||||||
departmentdel,
|
departmentdel,
|
||||||
departtree,
|
departtree,
|
||||||
} from '@/api/department'
|
} from '@/api/system/department'
|
||||||
|
import { userselect } from '@/api/system/user'
|
||||||
|
|
||||||
interface DeptItem {
|
interface DeptItem {
|
||||||
id?: number | string
|
id?: number | string
|
||||||
@@ -227,6 +228,7 @@ interface UserItem {
|
|||||||
username?: string
|
username?: string
|
||||||
realName?: string
|
realName?: string
|
||||||
nickName?: string
|
nickName?: string
|
||||||
|
nicke?: string
|
||||||
name?: string
|
name?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +287,18 @@ const formatCell = (_row: any, _column: any, value: any) => {
|
|||||||
return value === undefined || value === null || value === '' ? '暂无' : value
|
return value === undefined || value === null || value === '' ? '暂无' : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 根据leaderId获取用户的nicke字段
|
||||||
|
const getLeaderNicke = (leaderId: number | string | null | undefined): string => {
|
||||||
|
if (!leaderId) return '暂无'
|
||||||
|
const user = userOptions.value.find(u => u.id == leaderId)
|
||||||
|
return user?.nicke || user?.realName || user?.nickName || user?.username || user?.name || '暂无'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 部门领导列的formatter
|
||||||
|
const formatLeaderCell = (_row: any, _column: any, _value: any) => {
|
||||||
|
return getLeaderNicke(_row.leaderId)
|
||||||
|
}
|
||||||
|
|
||||||
// 构建树形选择器的选项
|
// 构建树形选择器的选项
|
||||||
const treeOptions = computed(() => {
|
const treeOptions = computed(() => {
|
||||||
const buildOptions = (nodes: DeptItem[], excludeId?: number | string): any[] => {
|
const buildOptions = (nodes: DeptItem[], excludeId?: number | string): any[] => {
|
||||||
@@ -347,15 +361,14 @@ const handleTreeReset = () => {
|
|||||||
// 加载用户列表(用于选择部门领导)
|
// 加载用户列表(用于选择部门领导)
|
||||||
const loadUsers = async () => {
|
const loadUsers = async () => {
|
||||||
try {
|
try {
|
||||||
// 这里需要根据实际API调整,暂时使用空数组
|
const res: any = await userselect({})
|
||||||
// 如果有用户列表API,可以在这里调用
|
// 兼容多种返回格式:res.data 或 res 本身
|
||||||
// const res: any = await userlist({ pageNum: 1, pageSize: 9999 })
|
const data = Array.isArray(res?.data) ? res.data : Array.isArray(res) ? res : []
|
||||||
// const data = res?.data ?? res ?? {}
|
userOptions.value = data
|
||||||
// const records = data.records || data.list || data.rows || data.items || []
|
|
||||||
// userOptions.value = Array.isArray(records) ? records : []
|
|
||||||
userOptions.value = []
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('load user list error', error)
|
console.error('load user list error', error)
|
||||||
|
ElMessage.error('加载用户列表失败')
|
||||||
|
userOptions.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +376,11 @@ const loadUsers = async () => {
|
|||||||
const loadList = async () => {
|
const loadList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 确保用户列表已加载(用于显示部门领导名称)
|
||||||
|
if (userOptions.value.length === 0) {
|
||||||
|
await loadUsers()
|
||||||
|
}
|
||||||
|
|
||||||
// 构建查询参数,过滤空字符串
|
// 构建查询参数,过滤空字符串
|
||||||
const params: any = {
|
const params: any = {
|
||||||
pageNum: pagination.pageNum,
|
pageNum: pagination.pageNum,
|
||||||
@@ -652,3 +670,4 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="log-page">
|
<div class="manage-log-page">
|
||||||
<el-card class="search-card" shadow="never">
|
<el-card class="search-card" shadow="never">
|
||||||
<div class="search-bar">
|
<div class="search-bar">
|
||||||
<el-form :inline="true" :model="queryForm" label-width="80px">
|
<el-form :inline="true" :model="queryForm" label-width="90px">
|
||||||
<el-form-item label="操作人">
|
<el-form-item label="日志名称">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryForm.operator"
|
v-model="queryForm.logName"
|
||||||
placeholder="输入操作人"
|
placeholder="输入日志名称"
|
||||||
clearable
|
clearable
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作类型">
|
<el-form-item label="日志类型">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryForm.operationType"
|
v-model="queryForm.logType"
|
||||||
placeholder="输入操作类型"
|
placeholder="输入日志类型"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建人ID">
|
||||||
|
<el-input
|
||||||
|
v-model="queryForm.createId"
|
||||||
|
placeholder="输入创建人ID"
|
||||||
clearable
|
clearable
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
@@ -38,51 +46,16 @@
|
|||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
>
|
>
|
||||||
<el-table-column type="index" label="序号" width="70" />
|
<el-table-column type="index" label="序号" width="80" />
|
||||||
<el-table-column
|
<el-table-column prop="logName" label="日志名称" min-width="150" />
|
||||||
prop="operator"
|
<el-table-column prop="logType" label="日志类型" min-width="120" />
|
||||||
label="操作人"
|
<el-table-column prop="logContent" label="日志内容" min-width="200" show-overflow-tooltip />
|
||||||
min-width="120"
|
<el-table-column prop="logWritetime" label="日志记录时间" min-width="180">
|
||||||
:formatter="formatCell"
|
<template #default="scope">
|
||||||
/>
|
{{ formatDateTime(scope.row.logWritetime) }}
|
||||||
<el-table-column
|
</template>
|
||||||
prop="operationType"
|
</el-table-column>
|
||||||
label="操作类型"
|
<el-table-column prop="remark" label="备注" min-width="200" show-overflow-tooltip />
|
||||||
min-width="120"
|
|
||||||
:formatter="formatCell"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="operationContent"
|
|
||||||
label="操作内容"
|
|
||||||
min-width="200"
|
|
||||||
:formatter="formatCell"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="module"
|
|
||||||
label="操作模块"
|
|
||||||
min-width="120"
|
|
||||||
:formatter="formatCell"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="ipAddress"
|
|
||||||
label="IP地址"
|
|
||||||
min-width="140"
|
|
||||||
:formatter="formatCell"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="operationTime"
|
|
||||||
label="操作时间"
|
|
||||||
width="180"
|
|
||||||
:formatter="formatCell"
|
|
||||||
/>
|
|
||||||
<el-table-column
|
|
||||||
prop="remark"
|
|
||||||
label="备注"
|
|
||||||
min-width="150"
|
|
||||||
:formatter="formatCell"
|
|
||||||
show-overflow-tooltip
|
|
||||||
/>
|
|
||||||
<el-table-column label="操作" width="180" fixed="right">
|
<el-table-column label="操作" width="180" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
|
<el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
|
||||||
@@ -108,48 +81,42 @@
|
|||||||
v-model="drawerVisible"
|
v-model="drawerVisible"
|
||||||
:title="drawerTitle"
|
:title="drawerTitle"
|
||||||
direction="rtl"
|
direction="rtl"
|
||||||
:size="520"
|
:size="500"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="form"
|
:model="form"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
label-width="100px"
|
label-width="120px"
|
||||||
class="drawer-form"
|
class="drawer-form"
|
||||||
>
|
>
|
||||||
<el-form-item v-if="isEdit" label="日志ID">
|
<el-form-item v-if="isEdit" label="日志ID">
|
||||||
<el-input v-model="form.id" disabled />
|
<el-input v-model="form.id" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作人" prop="operator">
|
<el-form-item label="日志名称" prop="logName">
|
||||||
<el-input v-model="form.operator" placeholder="请输入操作人" />
|
<el-input v-model="form.logName" placeholder="请输入日志名称" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作类型" prop="operationType">
|
<el-form-item label="日志类型" prop="logType">
|
||||||
<el-input v-model="form.operationType" placeholder="请输入操作类型" />
|
<el-input v-model="form.logType" placeholder="请输入日志类型" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作内容" prop="operationContent">
|
<el-form-item label="日志内容" prop="logContent">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.operationContent"
|
v-model="form.logContent"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="请输入操作内容"
|
placeholder="请输入日志内容"
|
||||||
:rows="3"
|
:rows="4"
|
||||||
maxlength="500"
|
maxlength="500"
|
||||||
show-word-limit
|
show-word-limit
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="操作模块" prop="module">
|
<el-form-item label="日志记录时间" prop="logWritetime">
|
||||||
<el-input v-model="form.module" placeholder="请输入操作模块" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="IP地址" prop="ipAddress">
|
|
||||||
<el-input v-model="form.ipAddress" placeholder="请输入IP地址" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="操作时间" prop="operationTime">
|
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="form.operationTime"
|
v-model="form.logWritetime"
|
||||||
type="datetime"
|
type="datetime"
|
||||||
placeholder="请选择操作时间"
|
placeholder="选择日志记录时间"
|
||||||
style="width: 100%"
|
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DDTHH:mm:ss.SSSZ"
|
||||||
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注">
|
<el-form-item label="备注">
|
||||||
@@ -185,20 +152,19 @@ import {
|
|||||||
managelogdel,
|
managelogdel,
|
||||||
manageloglist,
|
manageloglist,
|
||||||
managelogupd,
|
managelogupd,
|
||||||
} from '@/api/manage-log'
|
} from '@/api/system/manage-log'
|
||||||
|
|
||||||
interface LogItem {
|
interface LogItem {
|
||||||
id?: number | string
|
id?: string | number
|
||||||
operator?: string
|
logName: string
|
||||||
operationType?: string
|
logType: string
|
||||||
operationContent?: string
|
logContent: string
|
||||||
module?: string
|
logWritetime?: string
|
||||||
ipAddress?: string
|
|
||||||
operationTime?: string
|
|
||||||
remark?: string
|
remark?: string
|
||||||
remarks?: string
|
createId?: string
|
||||||
createTime?: string
|
createTime?: string
|
||||||
updateTime?: string
|
updateTime?: string
|
||||||
|
updateId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -207,8 +173,9 @@ const tableData = ref<LogItem[]>([])
|
|||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
|
|
||||||
const queryForm = reactive({
|
const queryForm = reactive({
|
||||||
operator: '',
|
logName: '',
|
||||||
operationType: '',
|
logType: '',
|
||||||
|
createId: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
@@ -223,102 +190,84 @@ const isEdit = ref(false)
|
|||||||
const formRef = ref<FormInstance>()
|
const formRef = ref<FormInstance>()
|
||||||
const form = reactive<LogItem>({
|
const form = reactive<LogItem>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
operator: '',
|
logName: '',
|
||||||
operationType: '',
|
logType: '',
|
||||||
operationContent: '',
|
logContent: '',
|
||||||
module: '',
|
logWritetime: '',
|
||||||
ipAddress: '',
|
|
||||||
operationTime: '',
|
|
||||||
remark: '',
|
remark: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules: FormRules = {
|
const rules: FormRules = {
|
||||||
operator: [{ required: true, message: '请输入操作人', trigger: 'blur' }],
|
logName: [{ required: true, message: '请输入日志名称', trigger: 'blur' }],
|
||||||
operationType: [{ required: true, message: '请输入操作类型', trigger: 'blur' }],
|
logType: [{ required: true, message: '请输入日志类型', trigger: 'blur' }],
|
||||||
operationContent: [{ required: true, message: '请输入操作内容', trigger: 'blur' }],
|
logContent: [{ required: true, message: '请输入日志内容', trigger: 'blur' }],
|
||||||
|
logWritetime: [{ required: true, message: '请选择日志记录时间', trigger: 'change' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (dateTime?: string) => {
|
||||||
|
if (!dateTime) return '-'
|
||||||
|
try {
|
||||||
|
const date = new Date(dateTime)
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(date.getDate()).padStart(2, '0')
|
||||||
|
const hours = String(date.getHours()).padStart(2, '0')
|
||||||
|
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||||
|
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
|
} catch {
|
||||||
|
return dateTime
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
form.id = undefined
|
form.id = undefined
|
||||||
form.operator = ''
|
form.logName = ''
|
||||||
form.operationType = ''
|
form.logType = ''
|
||||||
form.operationContent = ''
|
form.logContent = ''
|
||||||
form.module = ''
|
form.logWritetime = ''
|
||||||
form.ipAddress = ''
|
|
||||||
form.operationTime = ''
|
|
||||||
form.remark = ''
|
form.remark = ''
|
||||||
formRef.value?.clearValidate()
|
formRef.value?.clearValidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatCell = (_row: any, _column: any, value: any) => {
|
|
||||||
if (value === 0) return 0
|
|
||||||
return value === undefined || value === null || value === '' ? '暂无' : value
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadList = async () => {
|
const loadList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
// 构建查询参数,过滤空字符串
|
|
||||||
const params: any = {
|
const params: any = {
|
||||||
pageNum: pagination.pageNum,
|
pageNum: pagination.pageNum,
|
||||||
pageSize: pagination.pageSize,
|
pageSize: pagination.pageSize,
|
||||||
}
|
}
|
||||||
if (queryForm.operator && queryForm.operator.trim()) {
|
|
||||||
params.operator = queryForm.operator.trim()
|
|
||||||
}
|
|
||||||
if (queryForm.operationType && queryForm.operationType.trim()) {
|
|
||||||
params.operationType = queryForm.operationType.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 只添加有值的查询参数
|
||||||
|
if (queryForm.logName) params.logName = queryForm.logName
|
||||||
|
if (queryForm.logType) params.logType = queryForm.logType
|
||||||
|
if (queryForm.createId) params.createId = queryForm.createId
|
||||||
|
|
||||||
const res: any = await manageloglist(params)
|
const res: any = await manageloglist(params)
|
||||||
|
const data = res?.data ?? res ?? {}
|
||||||
console.log('操作日志列表API返回数据:', res)
|
|
||||||
|
// 兼容多种返回格式:{data:{records,total}} | {records,total} | {data:[]} | []
|
||||||
// 根据返回格式处理数据
|
|
||||||
let pageData = res?.data
|
|
||||||
|
|
||||||
// 如果res.data不存在,可能是直接返回了分页数据
|
|
||||||
if (!pageData && (res?.records || res?.total !== undefined)) {
|
|
||||||
pageData = res
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容多种返回格式
|
|
||||||
const recordsFromData =
|
const recordsFromData =
|
||||||
pageData?.records ||
|
data.records ||
|
||||||
pageData?.list ||
|
data.list ||
|
||||||
pageData?.rows ||
|
data.rows ||
|
||||||
pageData?.items ||
|
data.items ||
|
||||||
(Array.isArray(pageData?.data) ? pageData.data : undefined)
|
(Array.isArray(data.data) ? data.data : undefined)
|
||||||
|
|
||||||
const records = Array.isArray(recordsFromData)
|
const records = Array.isArray(recordsFromData)
|
||||||
? recordsFromData
|
? recordsFromData
|
||||||
: Array.isArray(pageData)
|
: Array.isArray(data)
|
||||||
? pageData
|
? data
|
||||||
: Array.isArray(res?.data)
|
: []
|
||||||
? res.data
|
|
||||||
: []
|
|
||||||
|
|
||||||
const totalValue =
|
const totalValue =
|
||||||
pageData?.total ??
|
data.total ?? data.count ?? data.totalCount ?? records.length ?? 0
|
||||||
pageData?.count ??
|
|
||||||
pageData?.totalCount ??
|
|
||||||
res?.total ??
|
|
||||||
(Array.isArray(records) ? records.length : 0)
|
|
||||||
|
|
||||||
tableData.value = records
|
tableData.value = records
|
||||||
total.value = Number(totalValue) || 0
|
total.value = Number(totalValue) || 0
|
||||||
|
} catch (error) {
|
||||||
console.log('解析后的数据:', {
|
|
||||||
records: records.length,
|
|
||||||
total: total.value,
|
|
||||||
firstRecord: records[0]
|
|
||||||
})
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('load log list error', error)
|
console.error('load log list error', error)
|
||||||
const errorMsg = error?.message || error?.msg || '加载操作日志列表失败'
|
ElMessage.error('获取日志列表失败')
|
||||||
ElMessage.error(errorMsg)
|
|
||||||
tableData.value = []
|
|
||||||
total.value = 0
|
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -330,8 +279,9 @@ const handleSearch = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
queryForm.operator = ''
|
queryForm.logName = ''
|
||||||
queryForm.operationType = ''
|
queryForm.logType = ''
|
||||||
|
queryForm.createId = ''
|
||||||
handleSearch()
|
handleSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,18 +306,11 @@ const openDrawer = async (mode: 'create' | 'edit', row?: LogItem) => {
|
|||||||
const res: any = await managelogbyid(row.id)
|
const res: any = await managelogbyid(row.id)
|
||||||
const data = res?.data || res || {}
|
const data = res?.data || res || {}
|
||||||
form.id = data.id ?? row.id
|
form.id = data.id ?? row.id
|
||||||
form.operator = data.operator ?? row.operator ?? ''
|
form.logName = data.logName ?? row.logName ?? ''
|
||||||
form.operationType = data.operationType ?? row.operationType ?? ''
|
form.logType = data.logType ?? row.logType ?? ''
|
||||||
form.operationContent = data.operationContent ?? row.operationContent ?? ''
|
form.logContent = data.logContent ?? row.logContent ?? ''
|
||||||
form.module = data.module ?? row.module ?? ''
|
form.logWritetime = data.logWritetime ?? row.logWritetime ?? ''
|
||||||
form.ipAddress = data.ipAddress ?? row.ipAddress ?? ''
|
form.remark = data.remark ?? row.remark ?? ''
|
||||||
form.operationTime = data.operationTime ?? row.operationTime ?? ''
|
|
||||||
form.remark =
|
|
||||||
data.remark ??
|
|
||||||
data.remarks ??
|
|
||||||
row.remark ??
|
|
||||||
(row as any)?.remarks ??
|
|
||||||
''
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
ElMessage.error('获取日志详情失败')
|
ElMessage.error('获取日志详情失败')
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false
|
||||||
@@ -375,28 +318,67 @@ const openDrawer = async (mode: 'create' | 'edit', row?: LogItem) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将时间格式转换为ISO 8601格式
|
||||||
|
const formatToISO = (dateTime?: string) => {
|
||||||
|
if (!dateTime) return new Date().toISOString()
|
||||||
|
try {
|
||||||
|
// 如果已经是ISO格式,直接返回
|
||||||
|
if (dateTime.includes('T') && dateTime.includes('Z')) {
|
||||||
|
return dateTime
|
||||||
|
}
|
||||||
|
// 如果是其他格式,转换为ISO格式
|
||||||
|
const date = new Date(dateTime)
|
||||||
|
return date.toISOString()
|
||||||
|
} catch {
|
||||||
|
return new Date().toISOString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const submitForm = () => {
|
const submitForm = () => {
|
||||||
formRef.value?.validate(async (valid) => {
|
formRef.value?.validate(async (valid) => {
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
submitLoading.value = true
|
submitLoading.value = true
|
||||||
try {
|
try {
|
||||||
const payload: any = {
|
// 获取当前时间(ISO 8601格式)
|
||||||
...form,
|
const now = new Date().toISOString()
|
||||||
remarks: form.remark,
|
|
||||||
|
// 获取用户ID(可以从localStorage或其他地方获取,这里先设为0)
|
||||||
|
const userId = 0 // 可以根据实际情况从用户store或localStorage获取
|
||||||
|
|
||||||
|
// 准备提交数据,按照后端Swagger要求补充所有字段
|
||||||
|
const submitData: any = {
|
||||||
|
logName: form.logName,
|
||||||
|
logType: form.logType,
|
||||||
|
logContent: form.logContent || '',
|
||||||
|
remark: form.remark || '',
|
||||||
|
logWritetime: formatToISO(form.logWritetime),
|
||||||
|
createId: userId,
|
||||||
|
updateId: userId,
|
||||||
|
createTime: now,
|
||||||
|
updateTime: now,
|
||||||
|
delSign: false, // 新增时delSign为false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是编辑,需要包含id
|
||||||
|
if (isEdit.value && form.id) {
|
||||||
|
submitData.id = Number(form.id)
|
||||||
|
} else {
|
||||||
|
// 新增时id设为0
|
||||||
|
submitData.id = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isEdit.value) {
|
if (isEdit.value) {
|
||||||
await managelogupd(payload)
|
await managelogupd(submitData)
|
||||||
ElMessage.success('更新成功')
|
ElMessage.success('更新成功')
|
||||||
} else {
|
} else {
|
||||||
await managelogadd(payload)
|
await managelogadd(submitData)
|
||||||
ElMessage.success('新增成功')
|
ElMessage.success('新增成功')
|
||||||
}
|
}
|
||||||
drawerVisible.value = false
|
drawerVisible.value = false
|
||||||
loadList()
|
loadList()
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('submit log error', error)
|
console.error('submit log error', error)
|
||||||
const errorMsg = error?.message || error?.msg || (isEdit.value ? '更新失败' : '新增失败')
|
const errorMsg = error?.response?.data?.message || error?.message || (isEdit.value ? '更新失败' : '新增失败')
|
||||||
ElMessage.error(errorMsg)
|
ElMessage.error(errorMsg)
|
||||||
} finally {
|
} finally {
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
@@ -409,7 +391,7 @@ const handleDelete = (row: LogItem) => {
|
|||||||
ElMessage.warning('缺少日志ID')
|
ElMessage.warning('缺少日志ID')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ElMessageBox.confirm(`确认删除操作日志吗?`, '提示', {
|
ElMessageBox.confirm(`确认删除日志「${row.logName}」吗?`, '提示', {
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
})
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
@@ -421,10 +403,9 @@ const handleDelete = (row: LogItem) => {
|
|||||||
await managelogdel(row.id)
|
await managelogdel(row.id)
|
||||||
ElMessage.success('删除成功')
|
ElMessage.success('删除成功')
|
||||||
loadList()
|
loadList()
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('delete log error', error)
|
console.error('delete log error', error)
|
||||||
const errorMsg = error?.message || error?.msg || '删除失败'
|
ElMessage.error('删除失败')
|
||||||
ElMessage.error(errorMsg)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
@@ -436,7 +417,7 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.log-page {
|
.manage-log-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -449,7 +430,7 @@ onMounted(() => {
|
|||||||
.search-bar {
|
.search-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
@@ -458,6 +439,7 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination {
|
.pagination {
|
||||||
@@ -471,5 +453,9 @@ onMounted(() => {
|
|||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-form {
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { ElMessage, ElMessageBox } 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 {
|
interface PositionItem {
|
||||||
id?: number | string
|
id?: number | string
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
import { onMounted, reactive, ref } from 'vue'
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { ElMessage, ElMessageBox } 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 {
|
interface RoleItem {
|
||||||
id?: number | string
|
id?: number | string
|
||||||
|
|||||||
527
rc_autoplc_front/src/views/user-role/index.vue
Normal file
527
rc_autoplc_front/src/views/user-role/index.vue
Normal 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>
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<el-form-item label="用户名">
|
<el-form-item label="用户名">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryForm.userName"
|
v-model="queryForm.userName"
|
||||||
placeholder="输入用户名(模糊查询)"
|
placeholder="输入用户名"
|
||||||
clearable
|
clearable
|
||||||
style="width: 200px"
|
style="width: 200px"
|
||||||
/>
|
/>
|
||||||
@@ -100,9 +100,10 @@
|
|||||||
min-width="150"
|
min-width="150"
|
||||||
:formatter="formatCell"
|
:formatter="formatCell"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="操作" width="180" fixed="right">
|
<el-table-column label="操作" width="250" fixed="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button type="primary" link @click="openDrawer('edit', scope.row)">编辑</el-button>
|
<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>
|
<el-button type="danger" link @click="handleDelete(scope.row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
:data="deptTreeOptions"
|
:data="deptTreeOptions"
|
||||||
:props="{ value: 'id', label: 'deptName', children: 'children' }"
|
:props="{ value: 'id', label: 'deptName', children: 'children' }"
|
||||||
check-strictly
|
check-strictly
|
||||||
placeholder="请选择部门(可选)"
|
placeholder="请选择部门"
|
||||||
clearable
|
clearable
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
@@ -182,7 +183,7 @@
|
|||||||
v-model="form.posId"
|
v-model="form.posId"
|
||||||
filterable
|
filterable
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择职位(可选)"
|
placeholder="请选择职位"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -213,6 +214,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-drawer>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -220,9 +246,11 @@
|
|||||||
import { onMounted, reactive, ref, computed } from 'vue'
|
import { onMounted, reactive, ref, computed } from 'vue'
|
||||||
import type { FormInstance, FormRules } from 'element-plus'
|
import type { FormInstance, FormRules } from 'element-plus'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { useradd, userbyid, userdel, userlist, userupd } from '@/api/user'
|
import { useradd, userbyid, userdel, userlist, userupd } from '@/api/system/user'
|
||||||
import { departtree } from '@/api/department'
|
import { departtree } from '@/api/system/department'
|
||||||
import { positionselect } from '@/api/position'
|
import { positionselect } from '@/api/system/position'
|
||||||
|
import { userRoleAdd, userRoleByUserId, userRoleDel } from '@/api/system/user-role'
|
||||||
|
import { rleselect } from '@/api/system/role'
|
||||||
|
|
||||||
interface UserItem {
|
interface UserItem {
|
||||||
id?: number | string
|
id?: number | string
|
||||||
@@ -253,6 +281,12 @@ interface PositionItem {
|
|||||||
posiCode?: string
|
posiCode?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RoleItem {
|
||||||
|
id?: number | string
|
||||||
|
roleName?: string
|
||||||
|
roleCode?: string
|
||||||
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const submitLoading = ref(false)
|
const submitLoading = ref(false)
|
||||||
const tableData = ref<UserItem[]>([])
|
const tableData = ref<UserItem[]>([])
|
||||||
@@ -261,6 +295,12 @@ const deptTreeData = ref<DeptItem[]>([])
|
|||||||
const positionOptions = ref<PositionItem[]>([])
|
const positionOptions = ref<PositionItem[]>([])
|
||||||
const deptMap = ref<Map<number | string, string>>(new Map())
|
const deptMap = ref<Map<number | string, string>>(new Map())
|
||||||
const posMap = 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({
|
const queryForm = reactive({
|
||||||
userName: '',
|
userName: '',
|
||||||
@@ -638,9 +678,128 @@ const handleDelete = (row: UserItem) => {
|
|||||||
.catch(() => {})
|
.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(() => {
|
onMounted(() => {
|
||||||
loadDeptTree()
|
loadDeptTree()
|
||||||
loadPositionList()
|
loadPositionList()
|
||||||
|
loadRoleList()
|
||||||
loadList()
|
loadList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user