在这篇博客中,我将带领大家一步一步地构建一个简易评分系统。这个项目会使用 Spring Boot 作为后端,Vue 作为前端框架,并结合 ElementUI 提供用户界面的组件。我们将详细介绍项目的设计思路和实现过程,并在此过程中学习如何将这些技术整合在一起。请系好安全带,准备好一起探索这个有趣的项目吧!
项目简介
评分系统是许多应用程序中的常见功能。无论是商品评价、文章评分,还是服务满意度调查,评分系统都能够帮助用户表达意见,并为其他用户提供参考。在这个项目中,我们将构建一个简易的评分系统,用户可以对特定项目进行评分,并查看所有评分的统计结果。
项目架构
首先,让我们来了解一下整个项目的架构。项目分为前后端两个部分:
- 后端(Spring Boot):负责处理业务逻辑、数据存储和 API 接口。
- 前端(Vue + ElementUI):负责展示用户界面,收集用户输入,并与后端交互。
目录结构
项目的目录结构如下:
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>
项目运行
到这里,我们已经完成了所有的代码编写。接下来,分别启动前后端服务:
- 启动 Spring Boot 后端服务:
bash
cd backend
mvn spring-boot:run
- 启动 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
组件、处理异常、验证用户输入、增加评分统计等高级功能。
希望这篇博客能帮助你更好地理解这些技术的使用,并激发你在实际项目中应用这些知识的灵感。如果你对这个项目有任何问题或建议,欢迎在评论区留言。感谢你的阅读,祝你编码愉快!