Skip to content

在后端的服务器没有搭建完成之前,无论我们做什么功能我们都需要自己mock数据。具体如下(这里只展示一个模块的模拟数据,其余类似):

typescript
import type { MockMethod } from 'vite-plugin-mock'

const userList = [
  {
    id: 1,
    nickName: '张三',
    userName: '张三',
    role: [
      {
        role: 1,
        roleName: '管理员',
      },
      {
        role: 2,
        roleName: '普通用户',
      },
    ],
  },
  {
    id: 2,
    nickName: '李四',
    userName: '李四',
    role: [
      {
        role: 1,
        roleName: '管理员',
      },
    ],
  },
  {
    id: 3,
    nickName: '王五',
    userName: '王五',
    role: [
      {
        role: 2,
        roleName: '普通用户',
      },
    ],
  },
]
export default [
  {
    url: '/mock/api/getUserList',
    method: 'get',
    response: () => {
      return {
        code: 0,
        message: 'success',
        data: userList,
      }
    },
  },
  {
    url: '/mock/api/editUser',
    method: 'post',
    response: ({ body }) => {
      const { id, nickName, role } = body
      const user = userList.find((item) => item.id === Number(id))
      if (user) {
        user.nickName = nickName
        user.role = role.map((item: number) => {
          return {
            role: item,
            roleName: item === 1 ? '管理员' : '普通用户',
          }
        })
      }
      const newUserList = userList.map((item) => {
        if (item.id === user?.id) {
          return user
        }
        return item
      })
      return {
        code: 0,
        message: '修改成功',
        data: newUserList,
      }
    },
  },
] as MockMethod[]

编写好mock数据以后,我们需要写好对应的接口,之后我们就可以正式开始功能开发了。

typescript
import RYRequest from '@/http'
import { IEditUser, IUserList, loginRequest, loginResponse } from './type'
import { BaseResponse } from '@/http/request/type'

export const userLogin = (data: loginRequest) =>
  RYRequest.post<BaseResponse<loginResponse>>({ url: '/login', data })

export const getUserList = () =>
  RYRequest.get<BaseResponse<IUserList[]>>({ url: '/getUserList' })

export const editUser = (data: IEditUser) =>
  RYRequest.post<BaseResponse<IUserList[]>>({
    url: '/editUser',
    data,
  })
typescript
export interface loginRequest {
  username: string
  password: string
}

export interface loginResponse {
  username: string
  accessToken: string
  roles: string[]
}

export interface reLoginRequest {
  accessToken: string
}

export interface IUserList {
  id: number
  nickName: string
  userName: string
  role: IRole[]
}

export interface IRole {
  role: number
  roleName: string
}

export interface IEditUser {
  id: number
  nickName: string
  role: number[]
}

一、查数据

查数据包含查全部数据、搜索、有些还包含分页。

1.1 查全部数据以及查指定数据

查全部数据很简单,我们只需要调用相对应的接口拿到数据,然后通过element-plus的table组件完成数据的渲染就可以了,当然了,一般来说我们都是先搭建好HTML结构之后,在获取数据、渲染数据的。

vue
<el-table :data="userList">
      <el-table-column prop="id" label="编号" />
      <el-table-column prop="nickName" label="昵称" />
      <el-table-column prop="role" label="权限">
        <template #default="{ row }: { row: IUserList }">
          <tag
            v-for="role in row.role"
            :key="role.role"
            :text="role.roleName"
            theme="#95d475"
          />
        </template>
      </el-table-column>
      <el-table-column label="操作">
        <template #default="{ row }">
          <el-button type="primary" size="small" @click="handleEdit(row)">
            编辑
          </el-button>
        </template>
      </el-table-column>
    </el-table>
vue
import { getUserList } from '@/api/user/user'
const userList = ref<IUserList[]>([])
const fetchUserList = async () => {
  const res = await getUserList()
  userList.value = res.data
}
onMounted(() => {
  fetchUserList()
})

完成上面两个步骤,就可以完成对应数据的表格展示了,展示完数据之后,为了满足用户的需求,我们还需要编写对应的搜索功能来提供给用户查指定条件的数据,跟上面一样的步骤,我们首先需要搭建对应的HTML结构,具体的界面实现如下: image.png 然后再实现对应逻辑,这里着重描述一下逻辑的实现,首先先看具体的逻辑实现:

vue
const handleSearch = () => {
  let res: IUserList[] = userList.value
  if (searchData.role || searchData.nickName) {
    if (searchData.nickName) {
      res = res.filter((item) => item.nickName.includes(searchData.nickName))
    }
    if (searchData.role) {
      res = searchData.nickName ? res : userList.value
      res = res.filter((item) => {
        return item.role.find((r) => r.role === searchData.role)
      })
    }
  } else {
    res = userList.value
  }
  userList.value = res
}
watch(
  [() => searchData.nickName, () => searchData.role],
  ([nickName, role]) => {
    if (!nickName || !role) {
      fetchUserList()
    }
  },
)

根据界面结构,我们可以知道用户可以根据两个条件来实现对数据的搜索,因此我们需要对两个数据分别进行判断,除此之外,我们还需要声明一个变量来保存所有的数据,防止对原来的数据进行修改。

1.2 数据的分页展示

为了体现与后端的配合,我们mock数据时提前将数据分页之后再发送给前端,前端根据后端提供的参数进行数据的渲染,具体的操作如下:

typescript
{
    url: '/mock/api/project',
    method: 'get',
    response: ({ query }) => {
      const { pageNum, pageSize } = query

      const length = data.length
      const totalPage = Math.ceil(length / pageSize)

      const startIndex = (pageNum - 1) * pageSize
      const endIndex = pageNum * pageSize

      const currentData = data.slice(startIndex, endIndex)

      return {
        code: 0,
        message: 'success',
        data: {
          list: currentData,
          pageNum: Number(pageNum),
          pageSize: Number(pageSize),
          total: length,
          totalPage,
        },
      }
    },
  },

这是项目中项目描述的数据接口,这个接口根据前端传递的当前页码,以及每页数据的数量两个参数对数据进行分页。编写好接口之后,我们在前端使用axios进行调用,具体如下:

typescript
export const getProjectList = (params: IParamsType) => {
  return ryRequest.get<BaseResponse<IProjectListType>>({
    url: '/project',
    params,
  })
}

有了以上这两步之后,我们就可以画HTML页面以及实现数据展示了,我们使用的是element-plus组件库的分页组件,使用这个组件,将从后端传递过来的参数传给这个组件,我们就可以实现分页功能了。具体实现如下:

typescript
<script setup lang="ts">
import { getProjectItem, getProjectList } from '@/api/project/project'
import { IProjectListItemType } from '@/api/project/type'
import { IPageInfo, ISearchData } from './type'

const pageInfo = reactive<IPageInfo>({
  currentPage: 1,
  pageSize: 5,
  total: 0,
})
const searchData = reactive<ISearchData>({
  title: '',
  introduce: '',
})
const tableData = ref<IProjectListItemType[]>([])
const fetchTableData = async (pageNum: number = 1, pageSize: number = 5) => {
  const result = await getProjectList({
    pageNum,
    pageSize,
  })
  tableData.value = result.data.list
  pageInfo.total = result.data.total
}
onMounted(() => {
  fetchTableData(pageInfo.currentPage, pageInfo.pageSize)
})

const handleCurrentChange = (val: number) => {
  pageInfo.currentPage = val
  fetchTableData(val, pageInfo.pageSize)
}

const handleSearch = async () => {
  if (searchData.title) {
    const res = await getProjectItem(searchData.title)
    tableData.value = res.data
  }
}
watch(
  () => searchData.title,
  (val) => {
    if (!val) {
      fetchTableData(pageInfo.currentPage, pageInfo.pageSize)
    }
  },
)
</script>
<template>
  <div class="home-container">
    <el-form :model="searchData" :inline="true">
      <el-form-item label="名称">
        <el-input v-model="searchData.title" placeholder="请输入名称" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleSearch">查询</el-button>
      </el-form-item>
    </el-form>
    <el-table :data="tableData">
      <el-table-column prop="id" label="编号" width="180" />
      <el-table-column prop="title" label="名称" width="180" />
      <el-table-column prop="introduce" label="介绍" />
    </el-table>
    <div class="pagination">
      <el-pagination
        v-model:current-page="pageInfo.currentPage"
        :page-size="pageInfo.pageSize"
        layout="prev, pager, next, jumper"
        :total="pageInfo.total"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>

二、添加数据以及修改数据

无论是添加数据还是修改数据,我们在后端的接口没有好之前,都要自己mock接口,使用axios获取我们mock的数据,这些步骤之前已经讲过,这里不再赘述,直接开始业务的开发,在后台管理系统中,添加数据一般都需要用到dialog弹出框,不过在element-plus组件库,他提供了一个新的组件MessageBox,这个组件比dialog更加简洁,我们可以直接使用JavaScript进行调用,具体逻辑如下:

typescript
const handleAdd = () => {
  ElMessageBox.prompt('请输入角色名称', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
  })
  .then(async ({ value }: { value: string }) => {
      await addRole(value)
      ElMessage.success('添加成功')
      await fetchRoleList()
    })
    .catch(() => {
      ElMessage.info('点击了取消')
    })
}

ElmessageBox.prompt返回一个promise,然后我们就可以实现添加数据的功能了。具体就是调用接口添加数据,然后刷新数据。 修改数据的界面采取dialog+form来实现,修改数据的具体步骤跟添加数据一样,都是调用接口修改,修改成功之后刷新数据,不过修改数据比添加数据多了一步数据回显,当用户点击编辑数据弹出框显示时,我们需要让用户看到修改前的数据,这里用到了对象的浅拷贝(不能将获取到的值直接赋值给变量,这样会让变量失去响应),具体操作如下:

javascript
const handleEdit = (row: IUserList) => {
  dialogVisible.value = true
  Object.assign(editData, {
    ...row,
    role: row.role.map((r) => r.role),
  })
}

当然除了Object.assign这个比较方便方法以外,我们也可以一个一个赋值,或者使用展开运算符,这里不过多描述。完成数据回显以后,我们要做的就是掉接口了。除此之外,添加数据也能够使用dialog+form的方式来实现,具体与修改数据类似,不过如果添加与修改共用一个dialog时,需要将编辑时保存的数据清空。

三、删除数据

删除数据应该是后台管理系统中最简单的了,我们在点击删除按钮时需要将点击数据的id传给后端就行了,然后后端将删除后的数据返回回来,我们将返回来的值赋值给变量就行了。具体的逻辑如下:

javascript
const handleDelete = async (id: number) => {
  const deleteRes = await deleteUser(id)
  userList.value = deleteRes.data
  ElMessage.success(deleteRes.message)
}