前后端分离项目进阶1---前端

发布于:2025-07-22 ⋅ 阅读:(17) ⋅ 点赞:(0)

前期项目链接:项目1

在项目1的基础上,引入element组件库,让前端代码变得更美观。

一.项目简介:

前端采用Vue3+Element组件库,后端采用Spring boot+mybatis框架+mysql数据库

项目后端:链接

运行结果:

前期准备:

1)配置组件库及自动导入

1.在cmd里输入yarn add element-plus --save下载组件库插件和yarn add @element-plus/icons-vue安装图标

2.main.js文件配置

import './assets/main.css'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
//添加中文支持
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)
app.use(ElementPlus, {
    locale: zhCn,
})
app.mount('#app')

3.vite.config.js文件配置

import { fileURLToPath, URL } from 'node:url'

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'

import AutoImport from 'unplugin-auto-import/vite';//自动导入,如果想要配置还需要下面的安装命令

import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueDevTools(),
    AutoImport({
      imports: [
        'vue',
        'vue-router'
      ],
      resolvers: [
        ElementPlusResolver()
      ],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    },
  },
   
})

如果想要使用自动导入,不想每次调用组件和加载组件的时候导入组件,直接用可以在vite.config.js文件配置import AutoImport from 'unplugin-auto-import/vite';

接着,输入npm install -D unplugin-vue-components unplugin-auto-import命令来安装自动导入所依赖的插件。就可以了。

2)导入sql文件

我的项目就是一个简单的测试,只写了一张Admin表,就只有账号,密码和名称三个字段。


/*
 Navicat Premium Data Transfer

 Source Server         : 8.1
 Source Server Type    : MySQL
 Source Server Version : 80100 (8.1.0)
 Source Host           : localhost:3306
 Source Schema         : mybaitistest

 Target Server Type    : MySQL
 Target Server Version : 80100 (8.1.0)
 File Encoding         : 65001

 Date: 07/07/2025 10:35:02
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for admin
-- ----------------------------
DROP TABLE IF EXISTS `admin`;
CREATE TABLE `admin`  (
  `Account` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `Password` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `Name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`Account`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

二.前端设计

1) 目录结构

2) 关键代码解析:

1.admin.js

import axios from 'axios'

const API_URL = 'http://localhost:8080/api/admins'

export default {
  getAllAdmins() {
    return axios.get(API_URL)
  },
  getAdmin(account) {
    return axios.get(`${API_URL}/${account}`)
  },
  addAdmin(admin) {
    return axios.post(API_URL, admin)
  },
  updateAdmin(account, admin) {
    return axios.put(`${API_URL}/${account}`, admin)
  },
  deleteAdmin(account) {
    return axios.delete(`${API_URL}/${account}`)
  }
}

解析:

这里的代理地址(指向后端控制器的统一资源地址)为'http://localhost:8080/api/admins'。

后端

  @GetMapping
    public List<Admin> getAllAdmins() {
        return adminMapper.findAll();
    }

@GetMapping里没有设置地址,默认为根地址api/admins

@RequestMapping("/api/admins")

axios.get用来访问后端接口地址。

2.AdminManagement.vue

<template>
  <div class="admin-management">
    <h2>管理员管理</h2>
    
    <!-- 添加管理员表单 -->
    <el-form :model="form" label-width="120px" style="margin-bottom: 20px;">
      <el-form-item label="账号">
        <el-input v-model="form.account" style="width: 200px;"></el-input>
      </el-form-item>
      <el-form-item label="密码">
        <el-input v-model="form.password" style="width: 200px;"></el-input>
      </el-form-item>
      <el-form-item label="姓名">
        <el-input v-model="form.name" style="width: 200px;"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addAdmin">添加</el-button>
      </el-form-item>
    </el-form>

    <!-- 管理员列表 -->
    <el-table :data="admins" border style="width: 100%">
      <el-table-column prop="account" label="账号" width="180"></el-table-column>
      <el-table-column prop="name" label="姓名" width="180"></el-table-column>
      <el-table-column label="操作">
        <template #default="scope">
          <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
          <el-button size="small" type="danger" @click="handleDelete(scope.row.account)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 编辑对话框 -->
    <el-dialog v-model="dialogVisible" title="编辑管理员">
      <el-form :model="editForm">
        <el-form-item label="账号">
          <el-input v-model="editForm.account" disabled></el-input>
        </el-form-item>
        <el-form-item label="密码">
          <el-input v-model="editForm.password"></el-input>
        </el-form-item>
        <el-form-item label="姓名">
          <el-input v-model="editForm.name"></el-input>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="updateAdmin">确定</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import adminApi from '../api/admin'

export default {
  setup() {
    const admins = ref([])
    const form = ref({
      account: '',
      password: '',
      name: ''
    })
    const editForm = ref({
      account: '',
      password: '',
      name: ''
    })
    const dialogVisible = ref(false)

    const fetchAdmins = async () => {
      try {
        const response = await adminApi.getAllAdmins()
        admins.value = response.data
      } catch (error) {
        ElMessage.error('获取管理员列表失败')
      }
    }

    const addAdmin = async () => {
      try {
        await adminApi.addAdmin(form.value)
        ElMessage.success('添加成功')
        form.value = { account: '', password: '', name: '' }
        fetchAdmins()
      } catch (error) {
        ElMessage.error('添加失败')
      }
    }

    const handleEdit = (row) => {
      editForm.value = { ...row }
      dialogVisible.value = true
    }

    const updateAdmin = async () => {
      try {
        await adminApi.updateAdmin(editForm.value.account, editForm.value)
        ElMessage.success('更新成功')
        dialogVisible.value = false
        fetchAdmins()
      } catch (error) {
        ElMessage.error('更新失败')
      }
    }

    const handleDelete = async (account) => {
      try {
        await adminApi.deleteAdmin(account)
        ElMessage.success('删除成功')
        fetchAdmins()
      } catch (error) {
        ElMessage.error('删除失败')
      }
    }

    onMounted(() => {
      fetchAdmins()
    })

    return {
      admins,
      form,
      editForm,
      dialogVisible,
      addAdmin,
      handleEdit,
      updateAdmin,
      handleDelete
    }
  }
}
</script>

<style scoped>
.admin-management {
  padding: 20px;
}
</style>

解析:

1.这里的表格用的是elemt组件库里面的表格标签。这里的导入:
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import adminApi from '../api/admin'

可以删去,因为我设置了自动导入。

2.当点击编辑按钮的时候,会触发handleEdit方法,弹出编辑对话框。当点击删除按钮的时候,会触发handleDelete方法,删除一行数据。当点击添加按钮时,就会触发addAdmin方法。

当使用 <el-table> 的 scoped-slot(作用域插槽)时,Element UI 会自动向插槽传递一个 scope 对象

   <template #default="scope">
          <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
          <el-button size="small" type="danger" @click="handleDelete(scope.row.account)">删除</el-button>
        </template>
  • #default:Vue 3 的简写语法,等同于 v-slot:default,表示默认插槽。

  • ="scope":将插槽传递的属性对象命名为 scope(可自定义为其他名称,如 slotProps)。

  • 这样就可以用scope.row来访问当前行数据了,scope是响应式数据,修改scope.row会直接修改表格数据源。

3.editForm 是一个 响应式对象,用于临时存储和编辑表格中某一行的数据。它的本质是

一个普通的 JavaScript 对象(Plain Object),但通过 Vue 的 ref 或 reactive 包装后成为响应式对象。admins,form,dialogvisible同理。

4.dialogVisible 控制对话框显示/隐藏的逻辑是 Element Plus 组件库(基于 Vue 3)的内置功能。
  • 控制显示:当 dialogVisible 为 true 时,<el-dialog> 组件会显示;为 false 时隐藏。

  • 双向绑定:通过 v-model 与 <el-dialog> 绑定,无论是代码修改 dialogVisible 还是用户点击对话框的关闭按钮,都会自动同步状态。

这里采用的是Composition API 的标准用法,如果使用setup语法糖,就可以不需要手动return了,这里没用语法糖,return的作用是将数据暴露给模块,让模块可以访问。

5.:data="admins" 绑定的数据来源于 Vue 组件的响应式状态,具体是通过 setup() 函数中定义的 admins 变量。

在 <script> 部分的 setup() 函数中,admins 通过 ref 创建并初始化:

import { ref } from 'vue';
const admins = ref([]); // 初始化为空数组

ref([]):创建一个响应式引用,初始值为空数组。

value:在 JavaScript 中访问时需要 admins.value,但在模板中直接写 admins 即可(Vue 自动解包)。

在 fetchAdmins 方法中,通过 API 请求获取数据并赋值给 admins

const fetchAdmins = async () => {
  try {
    const response = await adminApi.getAllAdmins(); // 调用API
    admins.value = response.data; // 将返回数据赋值给 admins
  } catch (error) {
    ElMessage.error('获取管理员列表失败');
  }
};

adminApi.getAllAdmins():访问后端接口( GET /api/admins),返回管理员列表。

response.data:接口返回的 JSON 数据格式为:

[
  { "account": "admin1", "name": "张三" },
  { "account": "admin2", "name": "李四" }
]

3.index.js

import { createRouter, createWebHistory } from 'vue-router'
import AdminManagement from '../components/AdminManagement.vue'

const routes = [
  {
    path: '/',
    name: 'AdminManagement',
    component: AdminManagement
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

解析:

配置路由。Vue Router 支持 三种主要的路由历史管理方式,这里采用的是html5模式。

对比总结

特性 createWebHistory() createWebHashHistory() createMemoryHistory()
URL 美观度 高 (/path) 低 (/#/path) 无 URL 变化
服务器配置 必需 无需 无需
SEO 支持 友好 不友好 不适用
浏览器兼容性 IE10+ 全兼容 全兼容
典型使用场景 生产环境 (SPA/SSR) 静态托管/旧浏览器 SSR/测试

如何选择?

  1. 优先选 createWebHistory()
    如果控制服务器且需要专业部署(配置 Fallback 到 index.html)。

  2. 备选 createWebHashHistory()
    如果是静态网站托管(如 GitHub Pages)或需要兼容 IE9。

  3. 特殊场景用 createMemoryHistory()
    仅用于测试或 SSR 框架内部。

path: '/'表示当用户访问网站的根路径时,会匹配这个路由,表示默认访问的根路径路由。

4.App.vue

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: 20px;
}
</style>

解析:

<router-view> 会根据当前浏览器地址栏的 URL 路径,动态渲染匹配的路由组件


网站公告

今日签到

点亮在社区的每一天
去签到