下面我们要实现的分配角色的弹窗如下

先添加类型
admin.d.ts
ts
interface AdminObjItf {
id?: number
username?: string
nickName?: string
email?: string
password?: string
note?: string
status?: number
}
interface AdminRoleFormData {
userRoles?: RoleObjItf[]
roleLists?: RoleObjItf[]
}
role.d.ts
ts
interface RoleObjItf {
id: number
name: string
}
下面我们添加两个角色相关的接口,获取所有角色以及根据用户id获取角色
ts
import request from './request'
interface ManageResponse<T = null> {
code: number
message: string
data: T
}
type PromiseResponse<T = {}> = Promise<ManageResponse<T>>
interface AdminListParams {
keyword: string
pageNum: number
pageSize: number
}
interface AdminListResponse {
list: {}[]
pageNum: number
pageSize: number
total: number
totalPage: number
}
// 获取用户数据列表
export const getAdminListApi = (data: AdminListParams):PromiseResponse<AdminListResponse> => {
return request.get('/admin/list', {
params: data
})
}
// 修改指定用户信息
export const updateAdmin = (id: number, data: AdminObjItf):PromiseResponse => {
return request.post('/admin/update/' + id, data)
}
// 获取所有角色
export const getRoleListAll = ():PromiseResponse<RoleObjItf[]> => {
return request.get('/role/listAll')
}
// 根据用户id获取角色
export const getAdminRoleById = (id: number):PromiseResponse<RoleObjItf[]> => {
return request.get('/admin/role/' + id)
}
然后我们在mock里面实现这两个接口
新建两个json文件,一个存列表,一个存个人的
role-list.json
json
[
{
"adminCount": 0,
"createTime": "2018-09-29T05:55:39.000+00:00",
"description": "只能查看及操作商品",
"id": 1,
"loginTime": "2018-09-29T05:55:39.000+00:00",
"name": "商品管理员",
"sort": 0,
"status": 1
},
{
"adminCount": 0,
"createTime": "2018-09-29T05:55:39.000+00:00",
"description": "只能查看及操作订单",
"id": 2,
"loginTime": "2018-09-29T05:55:39.000+00:00",
"name": "订单管理员",
"sort": 0,
"status": 1
},
{
"adminCount": 0,
"createTime": "2018-09-29T05:55:39.000+00:00",
"description": "都能查看操作",
"id": 3,
"loginTime": "2018-09-29T05:55:39.000+00:00",
"name": "超级管理员",
"sort": 0,
"status": 1
}
]
role.json
json
[
{
"adminCount": 0,
"createTime": "2018-09-29T05:55:39.000+00:00",
"description": "只能查看及操作商品",
"id": 1,
"loginTime": "2018-09-29T05:55:39.000+00:00",
"name": "商品管理员",
"sort": 0,
"status": 1
},
{
"adminCount": 0,
"createTime": "2018-09-29T05:55:39.000+00:00",
"description": "只能查看及操作订单",
"id": 2,
"loginTime": "2018-09-29T05:55:39.000+00:00",
"name": "订单管理员",
"sort": 0,
"status": 1
}
]
mock实现
ts
import { type MockMethod } from 'vite-plugin-mock'
import fs from 'fs'
import path from 'path'
import { validateToken } from '../utils'
const adminDataFile = path.join(process.cwd(), 'src/mock/data/admin-list.json')
const roleListDataFile = path.join(process.cwd(), 'src/mock/data/role-list.json')
const roleDataFile = path.join(process.cwd(), 'src/mock/data/role.json')
const readAdmins = () => {
try {
const raw = fs.readFileSync(adminDataFile, 'utf-8')
return JSON.parse(raw) as Array<Record<string, any>>
} catch (e) {
console.error('读取用户数据失败', e)
return []
}
}
const writeAdmins = (list: Array<Record<string, any>>) => {
fs.writeFileSync(adminDataFile, JSON.stringify(list, null, 2), 'utf-8')
}
const readRoleLists = () => {
try {
const raw = fs.readFileSync(roleListDataFile, 'utf-8')
return JSON.parse(raw) as Array<Record<string, any>>
} catch (e) {
console.error('读取角色列表数据失败', e)
return []
}
}
const readRole = () => {
try {
const raw = fs.readFileSync(roleDataFile, 'utf-8')
return JSON.parse(raw) as Array<Record<string, any>>
} catch (e) {
console.error('读取角色数据失败', e)
return []
}
}
export default [
{
url: '/api/admin/list',
method: 'get',
response: ({ headers }: { headers: Record<string, string> }) => {
const tokenCheck = validateToken(headers)
if (!tokenCheck.valid) {
return tokenCheck.response
}
const adminList = readAdmins()
const pageSize = 10
const pageNum = 1
const total = adminList.length
const totalPage = Math.max(1, Math.ceil(total / pageSize))
return {
code: 200,
message: '获取用户数据列表',
data: {
list: adminList,
pageNum,
pageSize,
total,
totalPage
}
}
}
},
{
url: '/api/admin/update/:id',
method: 'post',
response: (options: {
headers: Record<string, string>
body: Record<string, unknown>
url: string
}) => {
const { headers, body, url } = options
const tokenCheck = validateToken(headers)
if (!tokenCheck.valid) {
return tokenCheck.response
}
const adminList = readAdmins()
const id = Number((url.match(/\/api\/admin\/update\/(\d+)/) || [])[1])
const target = adminList.find(item => item.id === id)
if (!target) {
return {
code: 404,
message: '未找到对应的用户',
data: null
}
}
// 合并更新,同时保留原有的 id
Object.assign(target, body, { id: target.id })
writeAdmins(adminList)
return {
code: 200,
message: '修改用户信息成功',
data: target
}
}
},
{
url: '/api/role/listAll',
method: 'get',
response: ({ headers }: { headers: Record<string, string> }) => {
const tokenCheck = validateToken(headers)
if (!tokenCheck.valid) {
return tokenCheck.response
}
const roleList = readRoleLists()
return {
code: 200,
message: '获取角色列表成功',
data: roleList
}
}
},
{
url: '/api/admin/role/:id',
method: 'get',
response: ({ headers }: { headers: Record<string, string> }) => {
const tokenCheck = validateToken(headers)
if (!tokenCheck.valid) {
return tokenCheck.response
}
const roleList = readRole()
return {
code: 200,
message: '获取角色成功',
data: roleList
}
}
}
] as MockMethod[]
最后我们来实现页面的逻辑
html
<template>
<div class=''>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="id" label="编号"/>
<el-table-column prop="username" label="账号"/>
<el-table-column prop="nickName" label="姓名"/>
<el-table-column prop="email" label="邮箱"/>
<el-table-column label="添加时间">
<template v-slot:default="scope">
{{ formateDate(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column label="最后登录">
<template v-slot:default="scope">
{{ formateDate(scope.row.loginTime) }}
</template>
</el-table-column>
<el-table-column label="是否启用">
<template v-slot:default="scope">
<el-switch v-model="scope.row.status" :active-value="1" :inactive-value="0"></el-switch>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="{row}">
<el-button type="text" @click="allocRole(row.id)">分配角色</el-button>
<el-button type="text" @click="editAdmin(row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 编辑 -->
<EditAdmin v-model:visible="visible" :form="rowData" @sure="getTableData"></EditAdmin>
<!-- 分配权限 -->
<EditRole v-model:visible="roleVisible" :form="roleData" @sure="getTableData"></EditRole>
</div>
</template>
<script lang='ts' setup>
import { reactive, toRefs } from 'vue'
import { getAdminListApi, getRoleListAll, getAdminRoleById } from '@/api/ums'
import { ElMessage } from 'element-plus'
import EditAdmin from './components/EditAdmin.vue'
import EditRole from './components/EditRole.vue'
const state = reactive<{
tableData: {}[]
visible: boolean,
rowData: AdminObjItf
roleVisible: boolean
roleData: AdminRoleFormData
}>({
tableData: [],
visible: false,
rowData: {},
roleVisible: false,
roleData: {}
})
let { tableData, visible, rowData, roleVisible, roleData } = toRefs(state)
const getTableData = () => {
getAdminListApi({
keyword: '',
pageNum: 1,
pageSize: 10
}).then((res) => {
if(res.code === 200) {
tableData.value = res.data.list
} else {
ElMessage.error('获取用户数据列表失败')
}
})
}
getTableData();
// 获取所有角色
getRoleListAll().then((res) => {
if(res.code === 200) {
roleData.value.roleLists = res.data
} else {
ElMessage.error('获取所有角色失败')
}
})
const addZero = (num: number) => {
return num > 9 ? num : '0' + num
}
// 格式化时间
const formateDate = (time: string | undefined) => {
if (!time) return '';
const date = new Date(time);
const year = date.getFullYear();
let month = addZero(date.getMonth() + 1);
let day = addZero(date.getDate());
let hour = addZero(date.getHours());
let min = addZero(date.getMinutes());
let sec = addZero(date.getSeconds());
return `${year}-${month}-${day} ${hour}:${min}:${sec}`
}
// 点击编辑按钮
const editAdmin = (row: AdminObjItf) => {
visible.value = true
rowData.value = row
}
// 点击分配权限按钮
const allocRole = (id: number) => {
getAdminRoleById(id).then(res => {
if(res.code === 200) {
roleVisible.value = true
roleData.value.userRoles = res.data
}
})
}
</script>
<style lang='less' scoped>
</style>
新建组件 EditRole.vue
html
<template>
<el-dialog v-model="dialogVisible" title="分配角色" width="420" :before-close="close">
<el-form :model="form" label-width="70px">
<el-form-item label="权限">
<el-select v-model="roles" multiple>
<el-option v-for="role in form.roleLists" :label="role.name" :value="role.id"></el-option>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="modifyRole">确定</el-button>
</div>
</template>
</el-dialog>
</template>
<script lang='ts' setup>
import { computed, reactive, toRefs, watch } from 'vue'
const props = defineProps<{
visible: boolean,
form: AdminRoleFormData
}>()
const state = reactive<{
roles: number[]
}>({
roles: []
})
const { roles } = toRefs(state)
// 拷贝form
watch(() => props.form.userRoles, () => {
roles.value = props.form.userRoles?.map(el => el.id) || []
})
const emit = defineEmits<{
(event: 'update:visible', value: boolean): void
(event: 'sure'): void
}>()
// 双向绑定 dialog 显示状态(emit 更新父组件)
const dialogVisible = computed({
get: () => props.visible,
set: (val: boolean) => emit('update:visible', val)
})
// 点击关闭
const close = () => {
dialogVisible.value = false
}
// 确定
const modifyRole = () => {
close()
}
</script>
<style lang='less' scoped>
</style>