使用 Spring Boot + Vue + ElementUI 构建简易评分系统

2024-07-27 23:03:58 浏览数 (1)

在这篇博客中,我将带领大家一步一步地构建一个简易评分系统。这个项目会使用 Spring Boot 作为后端,Vue 作为前端框架,并结合 ElementUI 提供用户界面的组件。我们将详细介绍项目的设计思路和实现过程,并在此过程中学习如何将这些技术整合在一起。请系好安全带,准备好一起探索这个有趣的项目吧!

项目简介

评分系统是许多应用程序中的常见功能。无论是商品评价、文章评分,还是服务满意度调查,评分系统都能够帮助用户表达意见,并为其他用户提供参考。在这个项目中,我们将构建一个简易的评分系统,用户可以对特定项目进行评分,并查看所有评分的统计结果。

项目架构

首先,让我们来了解一下整个项目的架构。项目分为前后端两个部分:

  1. 后端(Spring Boot):负责处理业务逻辑、数据存储和 API 接口。
  2. 前端(Vue ElementUI):负责展示用户界面,收集用户输入,并与后端交互。
目录结构

项目的目录结构如下:

代码语言:txt复制
rating-system/
├── backend/           # 后端代码
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   ├── resources/
│   │   └── test/
├── frontend/          # 前端代码
│   ├── public/
│   ├── src/
│   ├── package.json
├── README.md

后端开发

1. 创建 Spring Boot 项目

首先,我们需要创建一个新的 Spring Boot 项目。可以使用 Spring Initializr 生成项目模板,选择以下依赖项:

  • Spring Web
  • Spring Data JPA
  • H2 Database(或其他你喜欢的数据库)
  • Lombok

项目创建完成后,在 application.properties 文件中配置数据库连接:

代码语言:properties复制
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
2. 创建实体类和数据访问层

我们需要一个实体类来表示评分记录。创建一个名为 Rating 的实体类:

代码语言:java复制
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;

@Entity
@Data
public class Rating {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String itemName; // 被评分的项目名称
    private int score; // 评分
}

然后,创建一个 JPA 仓库接口来访问数据库:

代码语言:java复制
import org.springframework.data.jpa.repository.JpaRepository;

public interface RatingRepository extends JpaRepository<Rating, Long> {
}
3. 创建服务层和控制器

接下来,我们需要创建服务层和控制器来处理业务逻辑和 API 请求。服务层负责业务逻辑的处理,控制器负责接收客户端的请求并返回响应。

创建一个 RatingService 类:

代码语言:java复制
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class RatingService {

    @Autowired
    private RatingRepository ratingRepository;

    public List<Rating> getAllRatings() {
        return ratingRepository.findAll();
    }

    public Rating addRating(Rating rating) {
        return ratingRepository.save(rating);
    }
}

然后,创建一个 RatingController 类:

代码语言:java复制
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/ratings")
public class RatingController {

    @Autowired
    private RatingService ratingService;

    @GetMapping
    public List<Rating> getAllRatings() {
        return ratingService.getAllRatings();
    }

    @PostMapping
    public Rating addRating(@RequestBody Rating rating) {
        return ratingService.addRating(rating);
    }
}

前端开发

1. 创建 Vue 项目

首先,我们需要创建一个新的 Vue 项目。可以使用 Vue CLI 创建项目:

代码语言:bash复制
vue create frontend

在项目创建过程中,选择默认配置即可。项目创建完成后,安装 ElementUI:

代码语言:bash复制
npm install element-ui --save

然后,在 src/main.js 文件中引入 ElementUI:

代码语言:javascript复制
import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

new Vue({
  render: h => h(App),
}).$mount('#app');
2. 创建评分组件

接下来,我们创建一个评分组件,用于显示评分列表和添加新评分。创建一个名为 Rating.vue 的文件:

代码语言:vue复制
<template>
  <div>
    <el-form @submit.prevent="addRating">
      <el-form-item label="项目名称">
        <el-input v-model="newRating.itemName"></el-input>
      </el-form-item>
      <el-form-item label="评分">
        <el-rate v-model="newRating.score"></el-rate>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addRating">提交</el-button>
      </el-form-item>
    </el-form>
    <el-table :data="ratings">
      <el-table-column prop="itemName" label="项目名称"></el-table-column>
      <el-table-column prop="score" label="评分"></el-table-column>
    </el-table>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      newRating: {
        itemName: '',
        score: 0,
      },
      ratings: [],
    };
  },
  methods: {
    async fetchRatings() {
      const response = await axios.get('/api/ratings');
      this.ratings = response.data;
    },
    async addRating() {
      const response = await axios.post('/api/ratings', this.newRating);
      this.ratings.push(response.data);
      this.newRating.itemName = '';
      this.newRating.score = 0;
    },
  },
  mounted() {
    this.fetchRatings();
  },
};
</script>

在上面的代码中,我们使用 ElementUI 提供的表单和表格组件来创建评分界面,并使用 Axios 进行 API 请求。

3. 将组件集成到主应用

src/App.vue 文件中集成 Rating.vue 组件:

代码语言:vue复制
<template>
  <div id="app">
    <Rating />
  </div>
</template>

<script>
import Rating from './components/Rating.vue';

export default {
  name: 'App',
  components: {
    Rating,
  },
};
</script>

<style>
#app {
  padding: 20px;
}
</style>

项目运行

到这里,我们已经完成了所有的代码编写。接下来,分别启动前后端服务:

  1. 启动 Spring Boot 后端服务:
代码语言:bash复制
cd backend
mvn spring-boot:run
  1. 启动 Vue 前端服务:
代码语言:bash复制
cd frontend
npm run serve

打开浏览器访问 http://localhost:8080,你将看到评分系统的界面,用户可以添加评分并查看所有评分记录。

进一步完善评分系统

在上一部分,我们已经完成了一个简易评分系统的基本功能。然而,要让系统更加完善和实用,我们还需要注意一些细节问题,并介绍更多的知识点和实现思路。在这部分中,我们将深入探讨如何优化评分系统,包括使用 el-rate 组件、处理异常、验证用户输入、增加评分统计等。

使用 el-rate 组件

el-rate 是 ElementUI 提供的评分组件,使用非常简单,可以为用户提供直观的评分界面。接下来,我们将详细介绍如何在项目中使用 el-rate 组件,并进一步优化评分功能。

1. 优化评分组件

在前面的代码中,我们已经使用了 el-rate 组件来收集用户的评分。现在,让我们进一步优化这个组件,为用户提供更好的使用体验。

更新 Rating.vue 文件:

代码语言:vue复制
<template>
  <div>
    <el-form @submit.prevent="addRating" label-width="120px">
      <el-form-item label="项目名称" :error="errors.itemName">
        <el-input v-model="newRating.itemName"></el-input>
      </el-form-item>
      <el-form-item label="评分" :error="errors.score">
        <el-rate v-model="newRating.score"></el-rate>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addRating">提交</el-button>
      </el-form-item>
    </el-form>
    <el-table :data="ratings" style="margin-top: 20px;">
      <el-table-column prop="itemName" label="项目名称"></el-table-column>
      <el-table-column prop="score" label="评分"></el-table-column>
    </el-table>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      newRating: {
        itemName: '',
        score: 0,
      },
      ratings: [],
      errors: {
        itemName: '',
        score: '',
      },
    };
  },
  methods: {
    async fetchRatings() {
      const response = await axios.get('/api/ratings');
      this.ratings = response.data;
    },
    validateForm() {
      let isValid = true;
      this.errors = {
        itemName: '',
        score: '',
      };
      if (!this.newRating.itemName) {
        this.errors.itemName = '项目名称不能为空';
        isValid = false;
      }
      if (this.newRating.score <= 0) {
        this.errors.score = '评分不能为空';
        isValid = false;
      }
      return isValid;
    },
    async addRating() {
      if (!this.validateForm()) {
        return;
      }
      try {
        const response = await axios.post('/api/ratings', this.newRating);
        this.ratings.push(response.data);
        this.newRating.itemName = '';
        this.newRating.score = 0;
      } catch (error) {
        console.error('添加评分失败', error);
      }
    },
  },
  mounted() {
    this.fetchRatings();
  },
};
</script>

<style scoped>
/* 自定义样式 */
</style>

在这个优化后的组件中,我们添加了表单验证功能,确保用户输入的项目名称和评分不为空。同时,我们使用 el-form-item:error 属性来显示错误信息。

2. 增加评分统计

为了让用户更直观地了解评分情况,我们可以增加评分统计功能,比如显示平均评分和评分次数。让我们来实现这个功能。

Rating.vue 文件中增加以下代码:

代码语言:vue复制
<template>
  <div>
    <el-form @submit.prevent="addRating" label-width="120px">
      <el-form-item label="项目名称" :error="errors.itemName">
        <el-input v-model="newRating.itemName"></el-input>
      </el-form-item>
      <el-form-item label="评分" :error="errors.score">
        <el-rate v-model="newRating.score"></el-rate>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addRating">提交</el-button>
      </el-form-item>
    </el-form>
    <el-card style="margin-top: 20px;">
      <div slot="header">
        <span>评分统计</span>
      </div>
      <div>
        <p>平均评分:{{ averageScore }}</p>
        <p>评分次数:{{ ratings.length }}</p>
      </div>
    </el-card>
    <el-table :data="ratings" style="margin-top: 20px;">
      <el-table-column prop="itemName" label="项目名称"></el-table-column>
      <el-table-column prop="score" label="评分"></el-table-column>
    </el-table>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      newRating: {
        itemName: '',
        score: 0,
      },
      ratings: [],
      errors: {
        itemName: '',
        score: '',
      },
    };
  },
  computed: {
    averageScore() {
      if (this.ratings.length === 0) {
        return 0;
      }
      const totalScore = this.ratings.reduce((sum, rating) => sum   rating.score, 0);
      return (totalScore / this.ratings.length).toFixed(1);
    },
  },
  methods: {
    async fetchRatings() {
      const response = await axios.get('/api/ratings');
      this.ratings = response.data;
    },
    validateForm() {
      let isValid = true;
      this.errors = {
        itemName: '',
        score: '',
      };
      if (!this.newRating.itemName) {
        this.errors.itemName = '项目名称不能为空';
        isValid = false;
      }
      if (this.newRating.score <= 0) {
        this.errors.score = '评分不能为空';
        isValid = false;
      }
      return isValid;
    },
    async addRating() {
      if (!this.validateForm()) {
        return;
      }
      try {
        const response = await axios.post('/api/ratings', this.newRating);
        this.ratings.push(response.data);
        this.newRating.itemName = '';
        this.newRating.score = 0;
      } catch (error) {
        console.error('添加评分失败', error);
      }
    },
  },
  mounted() {
    this.fetchRatings();
  },
};
</script>

<style scoped>
/* 自定义样式 */
</style>

在这个更新后的组件中,我们添加了一个 el-card 来显示评分统计信息。通过计算 averageScore 计算属性,我们可以展示平均评分,并且在评分列表下方显示评分次数。

处理异常

在实际开发中,异常处理是必不可少的一部分。我们需要在前后端都处理好可能出现的异常,确保系统的稳定性和可靠性。

后端异常处理

在 Spring Boot 中,我们可以使用全局异常处理器来捕获和处理所有的异常。创建一个全局异常处理器类:

代码语言:java复制
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<?> handleException(Exception ex, WebRequest request) {
        return new ResponseEntity<>("服务器内部错误: "   ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    // 其他异常处理方法
}

这个类使用了 @ControllerAdvice 注解,可以捕获所有控制器中的异常,并统一处理。在这里,我们简单地返回了一个包含错误信息的响应。

前端异常处理

在前端,我们可以在 Axios 请求中捕获异常,并显示友好的错误提示。前面我们已经在 addRating 方法中添加了异常处理,现在让我们进一步优化这个方法。

代码语言:javascript复制
methods: {
  async fetchRatings() {
    try {
      const response = await axios.get('/api/ratings');
      this.ratings = response.data;
    } catch (error) {
      console.error('获取评分失败', error);
      this.$message.error('获取评分失败');
    }
  },
  async addRating() {
    if (!this.validateForm()) {
      return;
    }
    try {
      const response = await axios.post('/api/ratings', this.newRating);
      this.ratings.push(response.data);
      this.newRating.itemName = '';
      this.newRating.score = 0;
      this.$message.success('评分提交成功');
    } catch (error) {
      console.error('添加评分失败', error);
      this.$message.error('添加评分失败');
    }
  },
},

在这个优化后的方法中,我们使用 ElementUI 提供的 this.$message 方法显示成功或失败的提示信息。

用户验证

在某些场景下,我们可能需要对用户进行验证,以确保只有授权用户才能进行评分。为了简单起见,这里我们不实现完整的用户认证系统,但我们可以模拟一个简单的用户验证过程。

假设我们有一个简单的用户系统,用户在评分前需要输入用户名。我们可以在 Rating.vue 文件中添加一个用户名输入框,并在提交评分时进行简单验证。

更新 Rating.vue 文件:

代码语言:vue复制
<template>
  <div>
    <el-form @submit.prevent="addRating" label-width="120px">
      <el-form-item label="用户名" :error="errors.username">
        <el-input v-model="username"></el-input>
      </el-form-item>
      <el-form-item label="项目名称" :error="errors.itemName">
        <el-input v-model="newRating.itemName"></el-input>
      </el-form-item>
      <el-form-item label="评分" :error="errors.score">
        <el-rate v-model="newRating.score"></el-rate>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="addRating">提交</el-button>
      </el-form-item>
    </el-form>
    <el-card style="margin-top: 20px;">
      <div slot="header">
        <span>评分统计</span>
      </div>
      <div>
        <p>平均评分:{{ averageScore }}</p>
        <p>评分次数:{{ ratings.length }}</p>
      </div>
    </el-card>
    <el-table :data="ratings" style="margin-top: 20px;">
      <el-table-column prop="itemName" label="项目名称"></el-table-column>
      <el-table-column prop="score" label="评分"></el-table-column>
    </el-table>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  data() {
    return {
      username: '',
      newRating: {
        itemName: '',
        score: 0,
      },
      ratings: [],
      errors: {
        username: '',
        itemName: '',
        score: '',
      },
    };
  },
  computed: {
    averageScore() {
      if (this.ratings.length === 0) {
        return 0;
      }
      const totalScore = this.ratings.reduce((sum, rating) => sum   rating.score, 0);
      return (totalScore / this.ratings.length).toFixed(1);
    },
  },
  methods: {
    async fetchRatings() {
      try {
        const response = await axios.get('/api/ratings');
        this.ratings = response.data;
      } catch (error) {
        console.error('获取评分失败', error);
        this.$message.error('获取评分失败');
      }
    },
    validateForm() {
      let isValid = true;
      this.errors = {
        username: '',
        itemName: '',
        score: '',
      };
      if (!this.username) {
        this.errors.username = '用户名不能为空';
        isValid = false;
      }
      if (!this.newRating.itemName) {
        this.errors.itemName = '项目名称不能为空';
        isValid = false;
      }
      if (this.newRating.score <= 0) {
        this.errors.score = '评分不能为空';
        isValid = false;
      }
      return isValid;
    },
    async addRating() {
      if (!this.validateForm()) {
        return;
      }
      try {
        const response = await axios.post('/api/ratings', this.newRating);
        this.ratings.push(response.data);
        this.newRating.itemName = '';
        this.newRating.score = 0;
        this.$message.success('评分提交成功');
      } catch (error) {
        console.error('添加评分失败', error);
        this.$message.error('添加评分失败');
      }
    },
  },
  mounted() {
    this.fetchRatings();
  },
};
</script>

<style scoped>
/* 自定义样式 */
</style>

在这个更新后的组件中,我们添加了用户名输入框,并在提交评分时验证用户名是否为空。这样可以确保用户在评分前输入用户名。

部署和测试

在完成所有功能后,我们需要将项目部署到服务器上进行测试。这里简单介绍一下如何部署 Spring Boot 和 Vue 项目。

部署 Spring Boot 项目

将 Spring Boot 项目打包成可执行的 jar 文件:

代码语言:bash复制
cd backend
mvn clean package

然后,将生成的 jar 文件上传到服务器并运行:

代码语言:bash复制
java -jar backend/target/rating-system-0.0.1-SNAPSHOT.jar
部署 Vue 项目

将 Vue 项目打包成静态文件:

代码语言:bash复制
cd frontend
npm run build

然后,将生成的 dist 文件夹中的静态文件上传到服务器的 Web 服务器目录中(例如 Nginx 或 Apache)。

配置 Web 服务器(以 Nginx 为例):

代码语言:nginx复制
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        root /path/to/your/dist;
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

结论

在这篇博客中,我们详细介绍了如何使用 Spring Boot、Vue 和 ElementUI 构建一个简易评分系统。通过这个项目,你可以学习到如何构建前后端分离的应用程序,并将不同技术栈的组件整合在一起。我们还深入探讨了使用 el-rate 组件、处理异常、验证用户输入、增加评分统计等高级功能。

希望这篇博客能帮助你更好地理解这些技术的使用,并激发你在实际项目中应用这些知识的灵感。如果你对这个项目有任何问题或建议,欢迎在评论区留言。感谢你的阅读,祝你编码愉快!

0 人点赞