【Java框架】知识点汇总Day7:Spring Boot +Vue(持续更新)

Spring Boot + Vue

前后端分离开发

由原来的单体架构(前后端一起)变为前后端分离的架构

前端一个服务:负责页面展示,用户交互,不需要进行业务逻辑处理,不需要访问数据库

后端一个服务:负责业务逻辑处理,访问数据库,不需要考虑页面展示,用户交互

前端开发工具:

HBuilder、VSCode、WebStorm、IDEA

一、VSCode创建vue项目

1.1 打开VSCode终端

切换到要创建项目的文件夹 ,创建项目(项目名:vue001)

java 复制代码
PS D:\vue001> cd D:/
PS D:\> npm create vue@latest vue001

1.2 安装依赖并运行

java 复制代码
  cd D:\vue001
  npm install
  npm run dev

创建的项目是Vue 3 + Vite,也是现在的主流。

Vue3的页面代码要卸载App.Vue中,Vue2的写在index.html中

访问页面 http://localhost:5173/index

二、Vue学习

Vue本质上自动完成数据到视图的绑定

java 复制代码
<template>
  <div id="app">
    <h1>{{ title }}</h1>
    <input v-model="title"/>  <!-- 会出现一个文本框,修改这里,h1中的title也会随之改变 -->
    <button @click="test">{{ name }}</button>
  </div>
</template>

<script>
export default {
  //数据
  data() {
    return {
      title: 'test',
      name:'测试按钮'
    }
  },
  //方法
  methods:{
    test(){
      // alert('test')
      this.title = 'abc'
    }
  },
  //初始化方法:页面加载之后会调用的默认方法
  created(){
    alert(0) //不能写成000,会识别成八进制,报错
  }
}
</script>

三、Element UI

Element UI是一个前端的UI组件,预先封装好了很多UI的标签,可以直接使用。

Element UI是为Vue2设计的,Vue3使用Element Plus。使用Vite。

3.1 引入Element Plus的css、js

java 复制代码
npm install element-plus

mian.ts中引入js和css

java 复制代码
// 引入js
import ElementPlus from 'element-plus' 
//引入css
import 'element-plus/dist/index.css'
java 复制代码
createApp(App).use(ElementPlus)//注册组件 不能多次调用createApp(App)//
const app = createApp(App)
app.use(ElementPlus)   // 先注册
app.mount('#app')      // 再挂载

3.2 常用组件

复制代码
<<template>
  <div id="app">
    <h1>{{ title }}</h1>
    <input v-model="title" />
    <button @click="test">{{ name }}</button>
    
    <el-button type="primary">
      <el-icon class="el-icon--left"><Notification /></el-icon>
      Primary
    </el-button>
    
    <el-link type="success">success</el-link>
  </div>

  <!-- radio单选框 -->
  <div class="mb-2 ml-4">
    <el-radio-group v-model="radio1">
      <el-radio value="1" size="large">Option 1</el-radio>
      <el-radio value="2" size="large">Option 2</el-radio>
    </el-radio-group>
  </div>

  <!-- Cascader级联选择器 -->
  <div>
    <p>Child options expand when clicked (default)</p>
    <el-cascader v-model="value" :options="options" @change="handleChange" />
  </div>
  <div>
    <p>Child options expand when hovered</p>
    <el-cascader
      v-model="value"
      :options="options"
      :props="props"
      @change="handleChange"
    />
  </div>

  <!-- 日期选择器(官网示例) -->
  <el-radio-group v-model="size" aria-label="size control" class="mb-4">
    <el-radio-button value="large">large</el-radio-button>
    <el-radio-button value="default">default</el-radio-button>
    <el-radio-button value="small">small</el-radio-button>
  </el-radio-group>
  <div class="demo-date-picker">
    <div class="block">
      <span class="demonstration">Default</span>
      <el-date-picker
        v-model="value1"
        type="date"
        placeholder="Pick a day"
        :size="size"
      />
    </div>
    <div class="block">
      <span class="demonstration">Picker with quick options</span>
      <el-date-picker
        v-model="value2"
        type="date"
        placeholder="Pick a day"
        :disabled-date="disabledDate"
        :shortcuts="shortcuts"
        :size="size"
      />
    </div>
  </div>
  <!-- input输入框 -->
   <el-input v-model="input" style="width: 240px" placeholder="Please input" />
   <!-- upload上传器 -->
   <el-upload
    v-model:file-list="fileList"
    class="upload-demo"
    action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
    multiple
    :on-preview="handlePreview"
    :on-remove="handleRemove"
    :before-remove="beforeRemove"
    :limit="3"
    :on-exceed="handleExceed">
    <el-button type="primary">Click to upload</el-button>
    <template #tip>
      <div class="el-upload__tip">
        jpg/png files with a size less than 500KB.
      </div>
    </template>
  </el-upload>
  <!-- avator头像 -->
   <el-row class="demo-avatar demo-basic">
    <el-col :lg="12" :md="12">
      <div class="sub-title">circle</div>
      <div class="demo-basic--circle">
        <div class="block">
          <el-avatar :size="50" :src="circleUrl" />
        </div>
        <div v-for="size in sizeList" :key="size" class="block">
          <el-avatar :size="size" :src="circleUrl" />
        </div>
      </div>
    </el-col>
    </el-row>
    <!-- 带框table -->
     <el-table :data="tableData" border style="width: 100%">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>



<script setup lang="ts">
// table数据
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
]
// avator头像
import { reactive, toRefs } from 'vue'

const state = reactive({
  circleUrl:
    'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png',
  squareUrl:
    'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
  sizeList: ['small', '', 'large'] as const,
})

const { circleUrl, squareUrl, sizeList } = toRefs(state)
import { ref } from 'vue'

// ========== 你的原有数据 ==========
const title = ref('test')
const name = ref('测试按钮')

const test = () => {
  title.value = 'abc'
}
const input = ref('')

// 初始化
alert(0)

// ========== radio ==========
const radio1 = ref('1')

// ========== Cascader ==========
const value = ref<string[]>([])
const props = {
  expandTrigger: 'hover' as const,
}
const handleChange = (value: string[]) => {
  console.log(value)
}

const options = [
  {
    value: 'guide',
    label: 'Guide',
    children: [
      {
        value: 'disciplines',
        label: 'Disciplines',
        children: [
          { value: 'consistency', label: 'Consistency' },
          { value: 'feedback', label: 'Feedback' },
          { value: 'efficiency', label: 'Efficiency' },
          { value: 'controllability', label: 'Controllability' },
        ],
      },
      {
        value: 'navigation',
        label: 'Navigation',
        children: [
          { value: 'side nav', label: 'Side Navigation' },
          { value: 'top nav', label: 'Top Navigation' },
        ],
      },
    ],
  },
  {
    value: 'component',
    label: 'Component',
    children: [
      {
        value: 'basic',
        label: 'Basic',
        children: [
          { value: 'layout', label: 'Layout' },
          { value: 'color', label: 'Color' },
          { value: 'typography', label: 'Typography' },
          { value: 'icon', label: 'Icon' },
          { value: 'button', label: 'Button' },
        ],
      },
      {
        value: 'form',
        label: 'Form',
        children: [
          { value: 'radio', label: 'Radio' },
          { value: 'checkbox', label: 'Checkbox' },
          { value: 'input', label: 'Input' },
          { value: 'input-number', label: 'InputNumber' },
          { value: 'select', label: 'Select' },
          { value: 'cascader', label: 'Cascader' },
          { value: 'switch', label: 'Switch' },
          { value: 'slider', label: 'Slider' },
          { value: 'time-picker', label: 'TimePicker' },
          { value: 'date-picker', label: 'DatePicker' },
          { value: 'datetime-picker', label: 'DateTimePicker' },
          { value: 'upload', label: 'Upload' },
          { value: 'rate', label: 'Rate' },
          { value: 'form', label: 'Form' },
        ],
      },
      {
        value: 'data',
        label: 'Data',
        children: [
          { value: 'table', label: 'Table' },
          { value: 'tag', label: 'Tag' },
          { value: 'progress', label: 'Progress' },
          { value: 'tree', label: 'Tree' },
          { value: 'pagination', label: 'Pagination' },
          { value: 'badge', label: 'Badge' },
        ],
      },
      {
        value: 'notice',
        label: 'Notice',
        children: [
          { value: 'alert', label: 'Alert' },
          { value: 'loading', label: 'Loading' },
          { value: 'message', label: 'Message' },
          { value: 'message-box', label: 'MessageBox' },
          { value: 'notification', label: 'Notification' },
        ],
      },
      {
        value: 'navigation',
        label: 'Navigation',
        children: [
          { value: 'menu', label: 'Menu' },
          { value: 'tabs', label: 'Tabs' },
          { value: 'breadcrumb', label: 'Breadcrumb' },
          { value: 'dropdown', label: 'Dropdown' },
          { value: 'steps', label: 'Steps' },
        ],
      },
      {
        value: 'others',
        label: 'Others',
        children: [
          { value: 'dialog', label: 'Dialog' },
          { value: 'tooltip', label: 'Tooltip' },
          { value: 'popover', label: 'Popover' },
          { value: 'card', label: 'Card' },
          { value: 'carousel', label: 'Carousel' },
          { value: 'collapse', label: 'Collapse' },
        ],
      },
    ],
  },
  {
    value: 'resource',
    label: 'Resource',
    children: [
      { value: 'axure', label: 'Axure Components' },
      { value: 'sketch', label: 'Sketch Templates' },
      { value: 'docs', label: 'Design Documentation' },
    ],
  },
]

// ========== 官网日期选择器示例 ==========
const size = ref<'default' | 'large' | 'small'>('default')
const value1 = ref('')
const value2 = ref('')

const shortcuts = [
  {
    text: 'Today',
    value: new Date(),
  },
  {
    text: 'Yesterday',
    value: () => {
      const date = new Date()
      date.setTime(date.getTime() - 3600 * 1000 * 24)
      return date
    },
  },
  {
    text: 'A week ago',
    value: () => {
      const date = new Date()
      date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
      return date
    },
  },
]

const disabledDate = (time: Date) => {
  return time.getTime() > Date.now()
}
// upload上传器
import { ElMessage, ElMessageBox } from 'element-plus'

import type { UploadProps, UploadUserFile } from 'element-plus'

const fileList = ref<UploadUserFile[]>([
  {
    name: 'element-plus-logo.svg',
    url: 'https://element-plus.org/images/element-plus-logo.svg',
  },
  {
    name: 'element-plus-logo2.svg',
    url: 'https://element-plus.org/images/element-plus-logo.svg',
  },
])

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  console.log(file, uploadFiles)
}

const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
  console.log(uploadFile)
}

const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
  ElMessage.warning(
    `The limit is 3, you selected ${files.length} files this time, add up to ${
      files.length + uploadFiles.length
    } totally`
  )
}

const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
  return ElMessageBox.confirm(
    `Cancel the transfer of ${uploadFile.name} ?`
  ).then(
    () => true,
    () => false
  )
}
</script>

<style scoped>
.demo-basic {
  text-align: center;
}
.demo-basic .sub-title {
  margin-bottom: 10px;
  font-size: 14px;
  color: var(--el-text-color-secondary);
}
.demo-basic .demo-basic--circle,
.demo-basic .demo-basic--square {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.demo-basic .block:not(:last-child) {
  border-right: 1px solid var(--el-border-color);
}
.demo-basic .block {
  flex: 1;
}
.demo-basic .el-col:not(:last-child) {
  border-right: 1px solid var(--el-border-color);
}
@media screen and (max-width: 992px) {
  .demo-basic .el-col:not(:last-child) {
    border-right: none;
  }
}
.el-link {
  margin-right: 8px;
}
.demo-date-picker {
  display: flex;
  width: 100%;
  padding: 0;
  flex-wrap: wrap;
}
.demo-date-picker .block {
  padding: 1.5rem 0;
  text-align: center;
  border-right: solid 1px var(--el-border-color);
  flex: 1;
  min-width: 300px;
}
.demo-date-picker .block:last-child {
  border-right: none;
}
.demo-date-picker .demonstration {
  display: block;
  color: var(--el-text-color-secondary);
  font-size: 14px;
  margin-bottom: 1rem;
}
@media screen and (max-width: 768px) {
  .demo-date-picker .block {
    flex: 0 0 100%;
    padding: 1rem 0;
    min-width: auto;
    border-right: none;
    border-bottom: solid 1px var(--el-border-color);
  }
  .demo-date-picker .block:last-child {
    border-bottom: none;
  }
}
</style>

import './assets/main.css'

import { createApp } from 'vue'
// 引入js
import ElementPlus from 'element-plus' 
//引入css
import 'element-plus/dist/index.css'
//引入所有图标
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'

// createApp(App).mount('#app')
// createApp(App).use(ElementPlus)//注册组件  use必须在mount之前 先注册再挂载
const app = createApp(App)
// 循环注册所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}
app.use(ElementPlus)   // 先注册
app.mount('#app')      // 再挂载

四、前后端分离实战

springboot 2.7.11+ JDK11

我使用的IDE工具是IDEA2025.2 ,IDEA 2025.2 默认只支持 Spring Boot 3.x(要求 Java 17+)

但是学习+工作常用的是JDK11+springboot2.x。

如果创建项目,选择的是JDK26+Java17,又想更改。需要修改以下几个地方,才能跑起来,不然就一直报错:java: java.lang.ExceptionInInitializerError com.sun.tools.javac.code.TypeTag :: UNKNOWN(JDK 版本过高(26)与 Spring Boot 2.7.x / 编译器不兼容)

|-----------------------------------------------------|--------|
| 检查项 | 内容 |
| pom.xml中的<java.version> | 11 |
| Project Structure ->Project->SDK | JDK 11 |
| Project Structure ->Project ->Language level | 11 |
| Project Structure->Modules->Module SDK | JDK 11 |
| Settings ->Java Compiler->Target bytecode version | 11 |
| Settings ->Maven ->Importing ->JDK for importer | JDK 11 |
| Settings->Maven->Runner->JRE | JDK 11 |

4.1 pom.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.11</version>
    <relativePath/>
</parent>
<groupId>com.dyz</groupId>
<artifactId>springboot03</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot03</name>
<description>springboot03</description>

<properties>
    <java.version>11</java.version>
    <lombok.version>1.18.30</lombok.version>
</properties>

<dependencies>
    <!-- Thymeleaf 模板引擎 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- Web 支持(注意:是 web,不是 webmvc) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MySQL 驱动(2.7.x 用 mysql-connector-java,并指定版本) -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
        <scope>runtime</scope>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- 测试依赖(统一用 starter-test) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- MyBatis Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.3.2</version>
    </dependency>

    <!-- Velocity 模板(用于代码生成器) -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity</artifactId>
        <version>1.7</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>
</project>

4.2 生成器代码

java 复制代码
package com.dyz;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class Main {
    public static void main(String[] args) {
        AutoGenerator autoGenerator = new AutoGenerator();
        //配置数据源
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL);
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/car_rental_separate?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("123456");
        autoGenerator.setDataSource(dataSourceConfig);
        //全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOpen(false);
        globalConfig.setOutputDir(System.getProperty("user.dir") + ("/src/main/java"));
        globalConfig.setServiceName("%sService"); //%s替换符,避免出现IService不规范的情况
        autoGenerator.setGlobalConfig(globalConfig);
        //配置包
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.dyz");
        packageConfig.setEntity("entity");
        packageConfig.setController("controller");
        packageConfig.setMapper("mapper");
        packageConfig.setService("service");
        packageConfig.setServiceImpl("service.impl");
        autoGenerator.setPackageInfo(packageConfig);
        //生成策略
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setEntityLombokModel(true);
        strategyConfig.setInclude("sys_news");
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);//下划线转驼峰 SysNew,不是Sys_news这样不规范
        autoGenerator.setStrategy(strategyConfig);
        //启动
        autoGenerator.execute();
    }
}

4.3 启动类

java 复制代码
package com.dyz;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.dyz.mapper") //添加这行
public class Springboot03Application {

    public static void main(String[] args) {
        SpringApplication.run(Springboot03Application.class, args);
    }

}

4.4 application.yml

java 复制代码
spring:
  application:
    name: springboot03
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
#    url: jdbc:mysql://localhost:3306/sys
    url: jdbc:mysql://localhost:3306/car_rental_separate

4.5 controller实现方法

java 复制代码
package com.dyz.controller;


import com.dyz.entity.SysNews;
import com.dyz.mapper.SysNewsMapper;
import com.dyz.service.SysNewsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ${author}
 * @since 2026-06-02
 */
//@Controller
//@RequestMapping("//sysNews")
    @RestController
public class SysNewsController {
    @Autowired
   private SysNewsService sysNewsService;

    @GetMapping("/list")
    public List<SysNews> list(){
        return this.sysNewsService.list();
    }
}

4.6 浏览器访问

localhost:8080/list 是否拿到数据

4.7 前端list.vue页面

  • 安装路由和axios:
java 复制代码
npm install vue-router@4 axios
  • 创建src/router/index.js
javascript 复制代码
import { createRouter, createWebHistory } from 'vue-router'
import List from '../list.vue'

const routes = [
  {
    path: '/list',
    name: 'List',
    component: List
  },
  {
    path: '/',
    redirect: '/list'  // 默认跳转到 list 页面
  }
]

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

export default router
  • 创建src/main.js文件,跳过路由
javascript 复制代码
import './assets/main.css'
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'  // 引入路由

const app = createApp(App)

for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component)
}

app.use(ElementPlus)
app.use(router)   // 使用路由
app.mount('#app')
  • 修改App.vue只保留路由出口
javascript 复制代码
<<template>
  <router-view />
</template>

<script setup>
</script>
  • 创建src/list.vue页面
javascript 复制代码
<<template>
  <div style="padding: 20px;">
    <h2>数据列表</h2>
    <p v-if="errorMsg" style="color: red;">{{ errorMsg }}</p>
    <el-table :data="tableData" border v-loading="loading" style="width: 100%">
      <el-table-column prop="id" label="ID" width="80" align="center" />
      <el-table-column prop="title" label="标题" min-width="150" />
      <el-table-column prop="content" label="内容" min-width="200" show-overflow-tooltip />
      <el-table-column prop="createtime" label="创建时间" width="180" align="center" />
      <el-table-column prop="opername" label="操作人" width="120" align="center" />
       <el-table-column fixed="right" label="Operations" min-width="120">
      <template #default>
        <el-button link type="primary" size="small">Edit</el-button>
        <el-button
          link
          type="primary"
          size="small"
          @click.prevent="deleteRow(scope.$index)"
        >
          Remove
        </el-button>
      </template>
    </el-table-column>
    </el-table>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import axios from 'axios'

const tableData = ref([])
const loading = ref(false)
const errorMsg = ref('')

const getList = () => {
  loading.value = true
  errorMsg.value = ''
  
  axios.get('http://localhost:8080/list')
    .then(res => {
      // 关键:打印看后端到底返回什么结构
      console.log('=== 后端返回的数据 ===')
      console.log(res.data)
      
      // 根据实际返回结构调整
      tableData.value = res.data.data || res.data || []
    })
    .catch(err => {
      console.error('请求失败:', err)
      errorMsg.value = '请求失败: ' + err.message
    })
    .finally(() => {
      loading.value = false
    })
}

onMounted(() => {
  getList()
})
</script>

4.8 后端配置类实现跨域

java 复制代码
package com.dyz.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

//解决跨域问题
@Configuration
public class Corsconfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTION")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");

    }
}

4.9 浏览器localhost:5173/list数据

相关推荐
土狗TuGou1 小时前
SQL进阶笔记 · 第1篇:存储引擎
java·数据库·笔记·后端·sql·mysql
码语智行1 小时前
Spring Security自定义AuthenticationManager实现手机号/密码双认证
java·后端·spring
风之舞_yjf1 小时前
Vue基础(33)_web Storage(web存储)
前端·javascript·vue.js
夜空孤狼啸1 小时前
Vue Data UI:这不是图表库,是数据可视化 UI 平台
vue.js·ui·信息可视化
武子康1 小时前
Build-Your-Own-X 从零构建轻量级事件驱动微框架:嵌入式与物联网场景下的极简实践
人工智能·后端·物联网·ai·c#·大模型·嵌入式
空圆小生1 小时前
Vue3 + Spring Boot 全栈实战:从零搭建在线彩票模拟系统
java·spring boot·后端
小马爱打代码1 小时前
SpringBoot + 分布式锁 + 事务日志:跨服务操作原子性兜底方案
spring boot·分布式·后端
Rust研习社1 小时前
从 LaunchBadge 到 transact-rs:SQLx 社区迈出可持续治理的第一步
开发语言·后端·rust
真实的菜1 小时前
Spring Boot 2.2.x 优雅停机实践指南
spring boot·后端