教你搭建一个简洁易拓展的基于vite + vue3 + antd vue后台管理系统

1,455 阅读5分钟

前言:当我们要搭建一个后台管理系统时,选择一个简洁易用的模板是至关重要的。在本文中,我们将介绍如何使用 Vite、Vue 3 和 Ant Design Vue 来搭建一个简洁、通用且易扩展的后台管理系统模板。小编之前其实写过一个后台管理系统的相关文章,感兴趣的掘友们可以先去简单看一下,链接就放在下面了,至于为什么又要写一个新的同质的后台管理系统,emmmmm....,其实是上一篇我不满意,之前第三方库选用的是element,编码体验差强人意,这次就选了antd,而且也没配置eslint和prettier,后期的代码看起来乱成一锅粥了都,正好最近有项目要用,我就干脆重写一个了,废话不多说,我们直接步入正题吧。

juejin.cn/post/732459…

一. 项目搭建:

1.1 创建项目

npm create vite@latest 项目名称

image.png

1.2 安装初始配置和运行项目:

image.png

1.3 打开后如果效果如下,说明项目创建好了:

image.png

1.4 接下来通过VS Code对项目基本配置进行修改,如下图所示:

1.4.1:按需修改eslint代码检查规则:

image.png

1.4.2:安装必要插件:

image.png

1.4.3: ESLint 和 Prettier 配合使用:prettier官方提供了一款工具 eslint-config-prettier ,这个工具其实禁用掉了一些不必要的以及和Prettier相冲突的ESLint规则。此外,我们还需要安装如下依赖:
npm install --save-dev eslint-config-prettier
npm install --save-dev eslint-plugin-prettier
npm install --save-dev prettier

1.5:安装axios 和 antd

npm install axios
npm install ant-design-vue@next --save

antd推荐配置:

image.png

创建axios实例并进行相关配置(axios二次封装),比如开发环境跨域配置或者请求、响应拦截器等等

image.png

image.png

image.png

image.png

二. 项目基本结构搭建:

2.1: 创建必要文件夹并配置路由

image.png

image.png

2.2: layout容器的具体实现:

image.png

image.png

image.png

image.png

2.3: Home主页面的具体实现:

image.png

image.png

2.4: 登录页实现:

image.png

image.png

2.4: 角色管理页实现:这里小编就简单介绍一下吧,这个页面的代码也贴在下面了

image.png

image.png

image.png

image.png

image.png

image.png

image.png

<template>
  <div class="ele-body">
    <a-card :bordered="false">
      <!-- 搜索表单 -->
      <!-- <usersearch /> -->
      <a-form
        :label-col="{ xl: 7, lg: 5, md: 7, sm: 4 }"
        :wrapper-col="{ xl: 17, lg: 19, md: 17, sm: 20 }"
      >
        <a-row :gutter="8">
          <a-col :xl="6" :lg="12" :md="12" :sm="24" :xs="24">
            <a-form-item label="用户账号">
              <a-input v-model:value.trim="form.roleName" placeholder="请输入用户账号" allow-clear
                ><template #suffix> <UserOutlined style="color: #ccc" /> </template
              ></a-input>
            </a-form-item>
          </a-col>

          <a-col :xl="6" :lg="12" :md="12" :sm="24" :xs="24">
            <a-form-item class="ele-text-right" :wrapper-col="{ span: 24 }">
              <a-space>
                <a-button style="background-color: #5cc750" type="primary" @click="searchUsers"
                  >查询</a-button
                >
                <!-- <a-button>重置</a-button> -->
              </a-space>
            </a-form-item>
          </a-col>
        </a-row>
      </a-form>
      <!-- 数据操作 -->
      <!-- <a-space style="margin-bottom: 10px">
        <a-button style="background-color: #5cc750" type="primary" class="ele-btn-icon">
          <template #icon>
            <plus-outlined />
          </template>
          <span>新建</span>
        </a-button>
        <a-button danger type="primary" class="ele-btn-icon">
          <template #icon>
            <delete-outlined />
          </template>
          <span>删除</span>
        </a-button>
      </a-space> -->
      <!-- 表格 -->
      <a-table
        :pagination="pagination"
        @change="handleTableChange"
        bordered
        row-key="userId"
        :dataSource="dataSource"
        :columns="columns"
        :scroll="{ x: 1000 }"
      >
        <template #headerCell="{ column }">
          <template v-if="column.key === 'checkbox'">
            <a-checkbox />
          </template>
          <template v-if="column.key === 'username'">
            <UserOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'phone'">
            <WhatsAppOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'sexName'">
            <ManOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'roles'">
            <UserSwitchOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'createTime'">
            <HistoryOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'status'">
            <PieChartOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
          <template v-if="column.key === 'action'">
            <SettingOutlined style="margin-right: 5px" />{{ column.title }}
          </template>
        </template>
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'checkbox'">
            <a-checkbox />
          </template>
          <template v-if="column.key === 'nickname'">
            <!-- <router-link :to="'/system/user/details?id=' + record.userId">
            </router-link> -->
            <div style="color: #1677ff">{{ record.nickname }}</div>
          </template>
          <template v-else-if="column.key === 'roles'">
            <a-tag v-for="item in record.roles" :key="item.roleId" color="blue">
              {{ item.roleName }}
            </a-tag>
          </template>
          <template v-else-if="column.key === 'status'">
            <a-switch
              :checked="record.status === 0"
              @change="(checked) => editStatus(checked, record)"
            />
          </template>
          <template v-else-if="column.key === 'action'">
            <a-space>
              <!-- <a>修改</a>
              <a-divider type="vertical" />
              <a>重置密码</a> -->
              <!-- <a-divider type="vertical" /> -->
              <a-popconfirm
                placement="topRight"
                @confirm="handleDelUser(record)"
                title="确定要删除此用户吗?"
                ok-text="是"
                cancel-text="否"
              >
                <a class="ele-text-danger">删除</a>
              </a-popconfirm>
            </a-space>
          </template>
        </template>
      </a-table>
    </a-card>
  </div>
</template>

<script setup>
import { ref, onMounted, reactive } from 'vue'
import { getUserAPI, delUserAPI, queryUserAPI } from '@/apis/user.js'
import {
  UserOutlined,
  WhatsAppOutlined,
  ManOutlined,
  UserSwitchOutlined,
  HistoryOutlined,
  SettingOutlined,
  PieChartOutlined
} from '@ant-design/icons-vue'
// import usersearch from './components/usersearch.vue'
import { message } from 'ant-design-vue'

// 查询用户相关
const form = ref({
  roleName: '',
  comments: ''
})
const queryData = ref({
  username: '',
  role: null
})
const searchUsers = async () => {
  console.log('form: ', form.value)
  queryData.value.username = form.value.roleName
  console.log('queryParams', queryData.value)
  const { username } = queryData.value
  console.log('username', username)
  const res = await queryUserAPI(queryData.value)
  console.log('query', res)
  dataSource.value = res?.data || []
  if (!dataSource.value.length) {
    console.log('无符合条件的数据')
    dataSource.value = data.value
    message.error('抱歉!没有符合条件的数据')
  } else {
    message.success('查询成功!')
    dataSource.value = res.data
  }
}

// 表格数据
const dataSource = ref([])

//获取用户列表数据并格式化字段
const data = ref([])
onMounted(async () => {
  const res = await getUserAPI()
  console.log('userList: ', res)
  data.value = res.data
  data.value = data.value.map((item, index) => {
    index++
    return { ...item, index }
  })
  dataSource.value = data.value
})

// 表格列配置
const columns = ref([
  // {
  //   key: 'checkbox',
  //   width: 48,
  //   align: 'center'
  // },
  {
    key: 'index',
    width: 70,
    align: 'center',
    dataIndex: 'index',
    title: '编号'
  },
  {
    title: '用户账号',
    dataIndex: 'username',
    sorter: true,
    showSorterTooltip: false,
    align: 'center',
    key: 'username'
  },

  // {
  //   title: '手机号',
  //   dataIndex: 'phone',
  //   sorter: true,
  //   showSorterTooltip: false,
  //   align: 'center',
  //   key: 'phone'
  // },
  // {
  //   title: '性别',
  //   dataIndex: 'sexName',
  //   width: 100,
  //   align: 'center',
  //   key: 'sexName'
  // },
  // {
  //   title: '角色',
  //   key: 'roles',
  //   align: 'center'
  // },
  // {
  //   title: '创建时间',
  //   dataIndex: 'createTime',
  //   sorter: true,
  //   showSorterTooltip: false,
  //   ellipsis: true,
  //   align: 'center',
  //   key: 'createTime'
  // },
  // {
  //   title: '状态',
  //   key: 'status',
  //   dataIndex: 'status',
  //   width: 100,
  //   align: 'center'
  // },
  {
    title: '操作',
    key: 'action',
    width: 220,
    align: 'center'
  }
])

//删除指定用户
const handleDelUser = async (record) => {
  console.log('del', record.username)
  const res = await delUserAPI(record.username)
  //删除失败测试
  // const res = await delUserAPI('zs')
  console.log('delUser: ', res)
  if (res.response?.status && res.response.status != 200) {
    message.error(res.response.data.data)
    return
  } else {
    message.success(res.data)
    //更新数据
    const res1 = await getUserAPI()
    console.log('userList: ', res1)
    dataSource.value = res1.data
    dataSource.value = dataSource.value.map((item, index) => {
      index++
      return { ...item, index }
    })
    return
  }
}

//修改用户状态

//对于每页数据的管理
let pagination = reactive({
  total: dataSource.value.length,
  current: 1,
  pageSize: 5,
  showSizeChanger: true,
  pageSizeOptions: ['5', '4', '3'],
  showQuickJumper: true
})
// 对每页数据显示的交互
let handleTableChange = (pagina) => {
  console.log('pagination.current 第几页', pagina.current) //第几页
  console.log('pagina.pageSize 一页多少 ', pagina.pageSize) //一页多少
  pagination.current = pagina.current
  pagination.pageSize = pagina.pageSize
}
</script>

2.4: 用户管理页和角色管理页面差不多,区别在于具体的函数实现,和页面这里就不再赘述了,后面我会贴上代码的github地址,感兴趣的掘友可以去查看一下(其实常规的角色管理页的话应该是显示有几种角色,然后可以为各个角色分配权限等等,而用户页就是对用户数据的展示与CRUD,只是小编目前的项目需求比较简单又很紧急,所以也只仅仅是满足了甲方的项目需求,不过,在此基础上增加如上功能也就是水到渠成的事情了。)

讲到这里其实这个后台搭建的其实差不多啦,简单展示一下页面效果就好了,最后再贴上项目的GitHub仓库地址

1.gif

1 (1).gif

微信图片_20240510230900.png

本项目的GitHub代码仓库地址如下,如果哪里有不合理的地方还请大佬在评论区指正谢谢,本人只是前端小菜鸡一枚,这个项目只是根据实际需求写的,有不少东西是一个后台管理系统应该有但是我没写上的,但是小编在开头给出的另外一篇文章就有比较全面的知识点,想系统学习的话可以去看看,我这篇文章只是让我的开发更规范化和利用antd这个我个人觉得更实用的组件库按照最近的实际项目需求进行重写一遍。

github.com/huangkaihao…