Vue3+Vite+TS后台项目 ~ 7.管理员列表

本文详细介绍了使用Vue.js构建后台管理系统的步骤,包括基础页面接口封装、侧边栏导航设置、路由配置、分页器组件封装、列表功能实现、添加/编辑管理员功能的开发。同时,文章涵盖了状态管理、数据加载提示、删除操作、用户状态修改等关键功能,并展示了如何优化用户体验。此外,还讨论了如何封装自定义分页器组件和弹窗组件,以提高代码复用性和易用性。
摘要由CSDN通过智能技术生成

唯有热爱, 可抵岁月长河


一、基础页面

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>



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后海 0_o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值