唯有热爱, 可抵岁月长河
一、基础页面
1. 接口封装
编辑 src / api / admin.ts
文件
/**
* 管理员相关请求模块
*/
import request from '@/utils/request'
import { IListParams, Admin, AdminPostData } from './types/admin'
export const getAdmins = (params: IListParams) => { // 管理员列表
return request<{
count: string
list: Admin[]
}>({
method: 'GET',
url: '/setting/admin',
params
})
}
export const createAdmin = (data: AdminPostData) => { // 添加管理员
return request({
method: 'GET',
url: '/setting/admin',
data
})
}
export const updateAdmin = (id: number, data: AdminPostData) => { // 编辑管理员
return request({
method: 'PUT',
url: `/setting/admin/${id}`,
data
})
}
export const deleteAdmin = (id: number) => { // 删除管理员
return request({
method: 'DELETE',
url: `/setting/admin/${id}`
})
}
export const updateAdminStatus = (id: number, status: number) => { // 更新管理员状态
return request({
method: 'PUT',
url: `/setting/set_status/${id}/${status}`
})
}
新建 src / api / types / admin.ts
文件
export interface IListParams {
page: number
limit: number
name: string
roles: string
status: 0 | 1 | ''
}
export interface Admin {
id: number
account: string
head_pic: string
pwd: string
real_name: string
roles: string
last_ip: string
last_time: number
add_time: number
login_count: number
level: number
status: number
is_del: number
_add_time: string
_last_time: string
statusLoading?: boolean
}
export interface AdminPostData {
account: string
conf_pwd: string
pwd: string
roles: number[]
status: 0 | 1
real_name: string
}
2. 侧边栏导航
编辑 src / layout / components / AppMenu / index.vue
文件
<template>
<el-menu
active-text-color="#2d8cf0"
background-color="#304156"
class="el-menu-vertical-demo"
default-active="2"
text-color="#bcc0c5"
:collapse="$store.state.isCollapse"
:unique-opened="false"
router
>
<el-menu-item index="/">
<el-icon>
<home-filled />
</el-icon>
<span>首页</span>
</el-menu-item>
<el-sub-menu index="1">
<template #title>
<el-icon>
<goods-filled />
</el-icon>
<span>商品</span>
</template>
<el-menu-item index="/product/product_list">
<span>商品列表</span>
</el-menu-item>
<el-menu-item index="/product/product_classify">
<span>商品分类</span>
</el-menu-item>
<el-menu-item index="/product/product_attr">
<span>商品规格</span>
</el-menu-item>
<el-menu-item index="/product/product_reply">
<span>商品评论</span>
</el-menu-item>
</el-sub-menu>
<el-sub-menu index="2">
<template #title>
<el-icon><tools /></el-icon>
<span>设置</span>
</template>
<el-sub-menu index="2-1">
<template #title>
<el-icon><stamp /></el-icon>
<span>权限管理</span>
</template>
<el-menu-item index="/setting/permission/admin">
<template #title>
管理员
</template>
</el-menu-item>
<el-menu-item index="/setting/permission/role">
<template #title>
角色
</template>
</el-menu-item>
<el-menu-item index="/setting/permission/rule">
<template #title>
规则
</template>
</el-menu-item>
</el-sub-menu>
</el-sub-menu>
</el-menu>
</template>
<script lang="ts" setup>
import { HomeFilled, GoodsFilled, Tools, Stamp } from '@element-plus/icons-vue'
</script>
<style lang="scss" scoped>
.el-menu {
border-right: none;
}
.el-menu:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
</style>
3. 路由配置
编辑 src / router / modules / setting.ts
文件
import { RouteRecordRaw, RouterView } from 'vue-router'
const routes: RouteRecordRaw = {
path: 'setting',
component: RouterView,
meta: {
title: '权限管理'
},
children: [
{
path: 'permission/admin',
name: 'permission-admin',
component: () => import('@/views/setting/permission/admin/index.vue'),
meta: { // 自定义路由元数据
title: '管理员'
}
},
{
path: 'permission/role',
name: 'permission-role',
component: () => import('@/views/setting/permission/role/index.vue'),
meta: {
title: '角色'
}
},
{
path: 'permission/rule',
name: 'permission-rule',
component: () => import('@/views/setting/permission/rule/index.vue'),
meta: {
title: '规则'
}
}
]
}
export default routes
4. 管理员列表页面
编辑 src / viesw / setting / permission / admin / inde.vue
文件
<template>
<el-card>
<template #header>
数据筛选
</template>
<el-form
:inline="true"
ref="form"
:model="listParams"
@submit.prevent="handleQuery"
>
<el-form-item label="状态">
<el-select
v-model="listParams.status"
placeholder="请选择"
clearable
>
<el-option
label="全部"
value=""
/>
<el-option
label="显示"
:value="1"
/>
<el-option
label="不显示"
:value="0"
/>
</el-select>
</el-form-item>
<el-form-item label="管理员名称">
<el-input
v-model="listParams.name"
clearable
placeholder="请输入身份昵称"
/>
</el-form-item>
<el-form-item>
<el-button native-type="submit">
查询
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin-top: 20px;">
<template #header>
<el-button
type="primary"
>
添加管理员
</el-button>
</template>
<el-table
:data="list"
stripe
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="real_name"
label="姓名"
/>
<el-table-column
prop="account"
label="账号"
/>
<el-table-column
prop="roles"
label="身份"
min-width="180"
>
<template #default="scope">
<el-space wrap>
<el-tag
class="role-tag"
v-for="(item) in scope.row.roles.split(',')"
:key="item"
>
{{ item }}
</el-tag>
</el-space>
</template>
</el-table-column>
<el-table-column
label="最后一次登录时间"
prop="last_time"
/>
<el-table-column
label="最后一次登录IP"
prop="last_ip"
/>
<el-table-column label="状态">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
:loading="scope.row.statusLoading"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column
label="操作"
fixed="right"
min-width="100"
align="center"
>
<template #default="scope">
<el-button
type="text"
@click="handleUpdate(scope.row.id)"
>
编辑
</el-button>
<el-popconfirm
title="确认删除吗?"
@confirm="handleDelete()"
>
<template #reference>
<el-button type="text">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- <app-pagination
class="admin-pagination"
v-model:page="listParams.page"
v-model:limit="listParams.limit"
:list-count="listCount"
:load-list="loadList"
/> -->
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:total="listCount"
:page-sizes="[2, 4, 6]"
v-model:currentPage="listParams.page"
v-model:pageSize="listParams.limit"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</el-card>
</template>
<script lang="ts" setup>
import { getAdmins } from '@/api/admin'
import { Admin, IListParams } from '@/api/types/admin'
import { onMounted, reactive, ref } from 'vue'
const list = ref<Admin[]>([]) // 列表数据
const listCount = ref(0)
const listParams = reactive({ // 列表数据查询参数
page: 1,
limit: 10,
name: '',
roles: '',
status: '' as IListParams['status']
})
onMounted(() => {
loadList()
})
const loadList = async () => { // 获取管理员列表
const data = await getAdmins(listParams)
list.value = data.list
listCount.value = Number(data.count)
}
const handleQuery = async () => { // 查询
loadList()
}
const handleSizeChange = (size: number) => { // 每页条数
listParams.limit = size
listParams.page = 1
loadList()
}
const handleCurrentChange = (page: number) => { // 当前页数
listParams.page = page
loadList()
}
</script>
<style lang="scss" scoped>
.el-pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>
5. 页面展示
二、封装分页器
引入组件未成功…
新建 src / components / Pagination / index.vue
文件
<template>
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[2, 4, 6]"
:total="listCount"
:current-page="props.page"
:page-size="props.limit"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</template>
<script lang="ts" setup>
import { PropType } from '@vue/runtime-core'
const props = defineProps({
page: { // 当前页码
type: Number,
default: 1
},
limit: { // 每页条数
type: Number,
default: 10
},
listCount: { // 总数
type: Number,
default: 0
},
loadList: { // 查询
type: Function,
default: () => {}
},
obj: {
type: Object as PropType<{a: string, b: number}>,
required: true
}
})
// interface propsType {
// page: number,
// limit: number,
// listCount: number,
// loadList: () => void
// }
// // const props = defineProps<propsType>()
// const props = withDefaults(defineProps<propsType>(), { // 指定默认值
// page: 1,
// limit: 10,
// listCount: 0,
// loadList: () => {}
// })
interface EmitsType {
(e: 'update: page', page: number):void
(e: 'update: limit', limit: number):void
}
// const emit = defineEmits(['update: page', 'update: limit'])
const emit = defineEmits<EmitsType>()
const handleSizeChange = (size: number) => { // 每页条数
emit('update: page', 1)
emit('update: limit', size)
props.loadList()
}
const handleCurrentChange = (page: number) => { // 当前页数
emit('update: page', page)
props.loadList()
}
</script>
<style lang="scss" scoped>
.el-pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>
新建 src / components / Pagination / index.ts
文件
import { App } from '@vue/runtime-dom'
import Component from './index.vue'
export default {
install (app: App) {
app.component('AppPagination', Component)
}
}
三、列表功能
1. loading
目的:列表加载缓慢时,对用户也相对友好
编辑 src / views / setting / permission / admin / index.vue
文件
...
<el-form
:disabled="listLoading"
...
<el-table
v-loading="listLoading"
...
<el-pagination
:disabled="listLoading"
...
const listLoading = ref(true)
...
listLoading.value = true
const data = await getAdmins(listParams).finally(() => { // 停止 loading 状态
listLoading.value = false
})
2. 删除
编辑 src / views / setting / permission / admin / index.vue
文件
<el-popconfirm
title="确认删除吗?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<el-button type="text">
删除
</el-button>
</template>
</el-popconfirm>
import { getAdmins, deleteAdmin } from '@/api/admin'
import { ElMessage } from 'element-plus'
const handleDelete = async (id: number) => { // 删除
await deleteAdmin(id)
ElMessage.success('删除成功')
loadList()
}
3. 修改用户状态
编辑 src / views / setting / permission / admin / index.vue
文件
...
<el-switch
v-model="scope.row.status"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
:loading="scope.row.statusLoading"
@change="handleStatusChange(scope.row)"
/>
...
const handleStatusChange = async (item: Admin) => { // 修改用户状态
item.statusLoading = true
await updateAdminStatus(item.id, item.status).finally(() => {
item.statusLoading = false
})
ElMessage.success(`${item.status === 1 ? '启用' : '禁用'}成功`)
}
编辑 src / api / types / admin.ts
文件
export interface Admin {
...
statusLoading?: boolean
}
四、添加 / 编辑 管理员
1. 添加/编辑 管理员
⑴. 弹窗组件
新建 src / views / setting / permission / admin / AdminForm.vue
文件
<template>
<el-dialog
ref="dialog"
:title="props.adminId ? '编辑管理员' : '添加管理员'"
width="30%"
@closed="handleDialogClosed"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<span>This is a message</span>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">Cancel</el-button>
<el-button
type="primary"
>Confirm</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import type { IElDialog } from '@/types/element-plus'
import type { PropType } from 'vue'
const props = defineProps({
adminId: { // 编辑的管理员 ID
type: Number as PropType<number | null>,
default: null
}
})
interface EmitsType {
(e: 'update:admin-id', value: number | null): void
}
const emit = defineEmits<EmitsType>()
const dialog = ref<IElDialog | null>(null)
const handleCancel = () => { // 取消
if (dialog.value) {
dialog.value.visible = false
}
}
const handleDialogClosed = () => { // 弹窗关闭后触发
emit('update:admin-id', null)
}
</script>
<style lang="scss" scoped>
</style>
⑵. 父组件引用
新建 src / views / setting / permission / index.vue
文件
<template>
<el-card>
<template #header>
数据筛选
</template>
<el-form
:inline="true"
ref="form"
:model="listParams"
:disabled="listLoading"
@submit.prevent="handleQuery"
>
<el-form-item label="状态">
<el-select
v-model="listParams.status"
placeholder="请选择"
clearable
>
<el-option
label="全部"
value=""
/>
<el-option
label="显示"
:value="1"
/>
<el-option
label="不显示"
:value="0"
/>
</el-select>
</el-form-item>
<el-form-item label="管理员名称">
<el-input
v-model="listParams.name"
clearable
placeholder="请输入身份昵称"
/>
</el-form-item>
<el-form-item>
<el-button native-type="submit">
查询
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin-top: 20px;">
<template #header>
<el-button
type="primary"
@click="fromVisible = true"
>
添加管理员
</el-button>
</template>
<el-table
v-loading="listLoading"
:data="list"
stripe
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="real_name"
label="姓名"
/>
<el-table-column
prop="account"
label="账号"
/>
<el-table-column
prop="roles"
label="身份"
min-width="180"
>
<template #default="scope">
<el-space wrap>
<el-tag
class="role-tag"
v-for="(item) in scope.row.roles.split(',')"
:key="item"
>
{{ item }}
</el-tag>
</el-space>
</template>
</el-table-column>
<el-table-column
label="最后一次登录时间"
prop="last_time"
/>
<el-table-column
label="最后一次登录IP"
prop="last_ip"
/>
<el-table-column label="状态">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
:loading="scope.row.statusLoading"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column
label="操作"
fixed="right"
min-width="100"
align="center"
>
<template #default="scope">
<el-button
type="text"
@click="handleUpdate(scope.row.id)"
>
编辑
</el-button>
<el-popconfirm
title="确认删除吗?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<el-button type="text">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- <app-pagination
class="admin-pagination"
v-model:page="listParams.page"
v-model:limit="listParams.limit"
:list-count="listCount"
:load-list="loadList"
/> -->
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:total="listCount"
:page-sizes="[2, 4, 6]"
:disabled="listLoading"
v-model:currentPage="listParams.page"
v-model:pageSize="listParams.limit"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- @closed="adminId = null" -->
<admin-form
v-model="fromVisible"
v-model:admin-id="adminId"
/>
</el-card>
</template>
<script lang="ts" setup>
import { getAdmins, deleteAdmin, updateAdminStatus } from '@/api/admin'
import { Admin, IListParams } from '@/api/types/admin'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import AdminForm from './AdminForm.vue'
const list = ref<Admin[]>([]) // 列表数据
const listCount = ref(0)
const listParams = reactive({ // 列表数据查询参数
page: 1,
limit: 10,
name: '',
roles: '',
status: '' as IListParams['status']
})
const listLoading = ref(true)
const fromVisible = ref(false)
const adminId = ref<number | null>(null)
onMounted(() => {
loadList()
})
const loadList = async () => { // 获取管理员列表
listLoading.value = true
const data = await getAdmins(listParams).finally(() => { // 停止 loading 状态
listLoading.value = false
})
data.list.forEach(item => { // 切换用户状态的 loading 效果
item.statusLoading = false
})
list.value = data.list
listCount.value = Number(data.count)
}
const handleQuery = async () => { // 查询
loadList()
}
const handleSizeChange = (size: number) => { // 每页条数
listParams.limit = size
listParams.page = 1
loadList()
}
const handleCurrentChange = (page: number) => { // 当前页数
listParams.page = page
loadList()
}
const handleDelete = async (id: number) => { // 删除
await deleteAdmin(id)
ElMessage.success('删除成功')
loadList()
}
const handleStatusChange = async (item: Admin) => { // 修改用户状态
item.statusLoading = true
await updateAdminStatus(item.id, item.status).finally(() => {
item.statusLoading = false
})
ElMessage.success(`${item.status === 1 ? '启用' : '禁用'}成功`)
}
const handleUpdate = (id: number) => { // 编辑
adminId.value = id
fromVisible.value = true
}
</script>
<style lang="scss" scoped>
.el-pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>
⑶. 类型补充
新建 src / types / element-plus.ts
文件
import { ElForm, ElDialog } from 'element-plus'
import type { FormItemRule } from 'element-plus/es/components/form/src/form.type'
export type IElForm = InstanceType<typeof ElForm>
export type IElDialog = InstanceType<typeof ElDialog>
export type IFormRule = Record<string, FormItemRule[]>
⑷. 页面样式
⑸. 封装弹窗组件
和之前一样, 全局引用组件的方法应该不对
新建 src / DialogForm / index.vue
文件
<template>
<el-dialog
ref="dialog"
width="50%"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<slot />
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">取 消</el-button>
<el-button
type="primary"
:loading="confirmLoading"
@click="handleConfirm"
>确 定</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import type { PropType } from 'vue'
import type { IElDialog } from '@/types/element-plus'
// interface EmitsType {
// (e: 'confirm'): void
// }
// const emit = defineEmits<EmitsType>()
const props = defineProps({
confirm: {
type: Function as PropType<() => Promise<void>>,
default: () => Promise.resolve()
}
})
const dialog = ref<IElDialog | null>(null)
const confirmLoading = ref(false)
const handleCancel = () => {
if (dialog.value) {
dialog.value.visible = false
}
}
const handleConfirm = async () => {
confirmLoading.value = true
// emit('confirm')
await props.confirm().finally(() => {
confirmLoading.value = false
})
}
</script>
<style lang="scss" scoped></style>
新建 src / DialogForm / index.ts
文件
import { App } from '@vue/runtime-dom'
import Component from './index.vue'
export default {
install (app: App) {
app.component('AppDialogForm', Component)
}
}
2. 添加/编辑 管理员
⑴. 封装请求接口
编辑 src / api / admin.ts
文件
/**
* 管理员相关请求模块
*/
import request from '@/utils/request'
import { IListParams, Admin, AdminPostData } from './types/admin'
import { IFormData } from './types/form'
export const getAdmins = (params: IListParams) => { // 管理员列表
return request<{
count: string
list: Admin[]
}>({
method: 'GET',
url: '/setting/admin',
params
})
}
export const createAdmin = (data: AdminPostData) => { // 添加管理员
return request({
method: 'GET',
url: '/setting/admin',
data
})
}
export const updateAdmin = (id: number, data: AdminPostData) => { // 编辑管理员
return request({
method: 'PUT',
url: `/setting/admin/${id}`,
data
})
}
export const deleteAdmin = (id: number) => { // 删除管理员
return request({
method: 'DELETE',
url: `/setting/admin/${id}`
})
}
export const updateAdminStatus = (id: number, status: number) => { // 更新管理员状态
return request({
method: 'PUT',
url: `/setting/set_status/${id}/${status}`
})
}
export const getRoles = () => { // 管理员角色
return request<IFormData>({
method: 'GET',
url: 'setting/admin/create'
}).then(data => {
const roles = data.rules.find(item => item.field === 'roles')
if (roles && roles.options) {
return roles.options
}
return []
})
}
export const getAdmin = (id: number) => { // 管理员信息
return request<IFormData>({
method: 'GET',
url: `setting/admin/${id}/edit`
}).then(data => {
const obj: Record<string, any> = {}
data.rules.forEach(item => {
obj[item.field] = item.value
})
return obj as AdminPostData
})
}
⑵. 类型补充
新建 src / api / types / form.ts
文件
// import { FormItemRule } from 'element-plus/packages/form/src/form.type'
import type { FormItemRule } from 'element-plus/es/components/form/src/form.type'
export interface ISelectOptions {
label: string
value: number
}
export interface IFormRule {
title: string
field: string
props?: Record<string, any>
type: string
validate?: FormItemRule[]
value: string
options?: ISelectOptions[]
}
export interface IFormData {
action: string
info: string
method: string
status: boolean
title: string
rules: IFormRule[]
}
⑶. 添加 / 编辑 弹窗
新建 src / views / setting / promission / admin / AdminForm.vue
文件
<template>
<el-dialog
ref="dialog"
:title="props.adminId ? '编辑管理员' : '添加管理员'"
width="50%"
@open="handleDialogOpen"
@closed="handleDialogClosed"
:close-on-click-modal="false"
:close-on-press-escape="false"
>
<el-form
ref="form"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item
label="管理员账号"
prop="account"
>
<el-input
v-model="formData.account"
placeholder="请输入管理员账号"
/>
</el-form-item>
<el-form-item
label="管理员密码"
prop="pwd"
>
<el-input
v-model="formData.pwd"
placeholder="请输入管理员密码"
/>
</el-form-item>
<el-form-item
label="确认密码"
prop="conf_pwd"
>
<el-input
v-model="formData.conf_pwd"
placeholder="请输入确认密码"
/>
</el-form-item>
<el-form-item
label="管理员姓名"
prop="real_name"
>
<el-input
v-model="formData.real_name"
placeholder="请输入管理员姓名"
/>
</el-form-item>
<el-form-item
label="管理员身份"
prop="roles"
>
<el-select
v-model="formData.roles"
multiple
placeholder="请选择管理员身份"
>
<el-option
v-for="item in roles"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-radio-group v-model="formData.status">
<el-radio
:label="1"
>
开启
</el-radio>
<el-radio
:label="0"
>
关闭
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCancel">取消</el-button>
<el-button
type="primary"
@click="handleSubmit"
:loading="submitLoading"
>确认</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { getRoles, getAdmin, createAdmin, updateAdmin } from '@/api/admin'
import type { IElDialog, IElForm, IFormRule } from '@/types/element-plus'
import type { PropType } from 'vue'
import type { ISelectOptions } from '@/api/types/form'
import { ElMessage } from 'element-plus'
const props = defineProps({
adminId: { // 编辑的管理员 ID
type: Number as PropType<number | null>,
default: null
}
})
interface EmitsType { // 对外发布自定义事件
(e: 'update:admin-id', value: number | null): void
(e: 'success'):void
}
const emit = defineEmits<EmitsType>()
const dialog = ref<IElDialog | null>(null)
const handleCancel = () => { // 取消
if (dialog.value) {
dialog.value.visible = false
}
}
const handleDialogClosed = () => { // 弹窗关闭后触发
emit('update:admin-id', null)
form.value?.clearValidate() // 清除校验结果
form.value?.resetFields() // 清除表单数据
}
const submitLoading = ref(false)
const formLoading = ref(false)
const form = ref<IElForm | null>(null)
const formData = ref({
account: '',
pwd: '',
conf_pwd: '',
roles: [] as number[],
status: 0 as 0 | 1,
real_name: ''
})
const roles = ref<ISelectOptions[]>([])
const formRules: IFormRule = {
account: [
{ required: true, message: '请输入管理员账号', trigger: 'blur' }
],
pwd: [
{ required: true, message: '请输入管理员密码', trigger: 'blur' }
],
conf_pwd: [
{ required: true, message: '请输入确认密码', trigger: 'blur' }
],
roles: [
{ required: true, message: '请选择管理员角色', trigger: 'blur' }
],
real_name: [
{ required: true, message: '请输入管理员姓名', trigger: 'blur' }
]
}
const loadRoles = async () => { // 获取管理员角色
const data = await getRoles()
roles.value = data
}
const handleDialogOpen = () => { // 弹窗打开时触发
formLoading.value = true
// loadRoles().finally(() => {
// formLoading.value = false
// })
// loadAdmin().finally(() => {
// formLoading.value = false
// })
Promise.all([loadRoles(), loadAdmin()]).finally(() => { // 两个异步请求,放入 Promise 中同时请求, 只有都执行完毕,才会向下执行
formLoading.value = false
})
}
const loadAdmin = async () => { // 获取管理员信息(编辑)
if (!props.adminId) {
return
}
const data = await getAdmin(props.adminId)
formData.value = data
}
const handleSubmit = async () => { // 确认(提交)
const valid = form.value?.validate // 表单校验
if (!valid) return
submitLoading.value = true
if (!props.adminId) {
// 添加管理员
await createAdmin(formData.value)
} else {
// 编辑管理员
await updateAdmin(props.adminId, formData.value)
}
emit('success')
ElMessage.success('保存成功')
submitLoading.value = false
}
</script>
<style lang="scss" scoped>
</style>
⑷. 父组件引用
新建 src / views / setting / promission / admin / index.vue
文件
<template>
<el-card>
<template #header>
数据筛选
</template>
<el-form
:inline="true"
ref="form"
:model="listParams"
:disabled="listLoading"
@submit.prevent="handleQuery"
>
<el-form-item label="状态">
<el-select
v-model="listParams.status"
placeholder="请选择"
clearable
>
<el-option
label="全部"
value=""
/>
<el-option
label="显示"
:value="1"
/>
<el-option
label="不显示"
:value="0"
/>
</el-select>
</el-form-item>
<el-form-item label="管理员名称">
<el-input
v-model="listParams.name"
clearable
placeholder="请输入身份昵称"
/>
</el-form-item>
<el-form-item>
<el-button native-type="submit">
查询
</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin-top: 20px;">
<template #header>
<el-button
type="primary"
@click="fromVisible = true"
>
添加管理员
</el-button>
</template>
<el-table
v-loading="listLoading"
:data="list"
stripe
style="width: 100%"
>
<el-table-column
prop="id"
label="ID"
/>
<el-table-column
prop="real_name"
label="姓名"
/>
<el-table-column
prop="account"
label="账号"
/>
<el-table-column
prop="roles"
label="身份"
min-width="180"
>
<template #default="scope">
<el-space wrap>
<el-tag
class="role-tag"
v-for="(item) in scope.row.roles.split(',')"
:key="item"
>
{{ item }}
</el-tag>
</el-space>
</template>
</el-table-column>
<el-table-column
label="最后一次登录时间"
prop="last_time"
/>
<el-table-column
label="最后一次登录IP"
prop="last_ip"
/>
<el-table-column label="状态">
<template #default="scope">
<el-switch
v-model="scope.row.status"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
:loading="scope.row.statusLoading"
@change="handleStatusChange(scope.row)"
/>
</template>
</el-table-column>
<el-table-column
label="操作"
fixed="right"
min-width="100"
align="center"
>
<template #default="scope">
<el-button
type="text"
@click="handleUpdate(scope.row.id)"
>
编辑
</el-button>
<el-popconfirm
title="确认删除吗?"
@confirm="handleDelete(scope.row.id)"
>
<template #reference>
<el-button type="text">
删除
</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!-- <app-pagination
class="admin-pagination"
v-model:page="listParams.page"
v-model:limit="listParams.limit"
:list-count="listCount"
:load-list="loadList"
/> -->
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:total="listCount"
:page-sizes="[2, 4, 6]"
:disabled="listLoading"
v-model:currentPage="listParams.page"
v-model:pageSize="listParams.limit"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
<!-- @closed="adminId = null" -->
<admin-form
v-model="fromVisible"
v-model:admin-id="adminId"
@success="handleFormSubmit"
/>
</el-card>
</template>
<script lang="ts" setup>
import { getAdmins, deleteAdmin, updateAdminStatus } from '@/api/admin'
import { Admin, IListParams } from '@/api/types/admin'
import { ElMessage } from 'element-plus'
import { onMounted, reactive, ref } from 'vue'
import AdminForm from './AdminForm.vue'
const list = ref<Admin[]>([]) // 列表数据
const listCount = ref(0)
const listParams = reactive({ // 列表数据查询参数
page: 1,
limit: 10,
name: '',
roles: '',
status: '' as IListParams['status']
})
const listLoading = ref(true)
const fromVisible = ref(false)
const adminId = ref<number | null>(null)
onMounted(() => {
loadList()
})
const loadList = async () => { // 获取管理员列表
listLoading.value = true
const data = await getAdmins(listParams).finally(() => { // 停止 loading 状态
listLoading.value = false
})
data.list.forEach(item => { // 切换用户状态的 loading 效果
item.statusLoading = false
})
list.value = data.list
listCount.value = Number(data.count)
}
const handleQuery = async () => { // 查询
loadList()
}
const handleSizeChange = (size: number) => { // 每页条数
listParams.limit = size
listParams.page = 1
loadList()
}
const handleCurrentChange = (page: number) => { // 当前页数
listParams.page = page
loadList()
}
const handleDelete = async (id: number) => { // 删除
await deleteAdmin(id)
ElMessage.success('删除成功')
loadList()
}
const handleStatusChange = async (item: Admin) => { // 修改用户状态
item.statusLoading = true
await updateAdminStatus(item.id, item.status).finally(() => {
item.statusLoading = false
})
ElMessage.success(`${item.status === 1 ? '启用' : '禁用'}成功`)
}
const handleUpdate = (id: number) => { // 编辑
adminId.value = id
fromVisible.value = true
}
const handleFormSubmit = () => { // 弹窗确认(提交)
fromVisible.value = false
loadList()
}
</script>
<style lang="scss" scoped>
.el-pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>