有两个实体类分别是指标和计划,指标有总金额和可用金额等属性,计划有用途,金额和指标ID等属性,在一个页面上面展示指标和下面展示计划,根据指标来新增计划,计划有增删改查功能,仿若依框架来完成,前端的vue和请求分离
时间: 2025-07-21 14:51:35 浏览: 2
<think>我们是在若依框架(RuoYi)前后端分离版本的基础上进行开发。若依框架是一个基于Spring Boot和Vue.js的权限管理系统,我们需要在其中新增两个实体类:指标(Indicator)和计划(Plan),并实现计划的增删改查功能,同时前端Vue页面要展示指标和计划信息,并且计划是基于指标来新增的。
步骤大致分为:
1. 后端实体类创建(Indicator和Plan)
2. 后端Mapper层(DAO层)
3. 后端Service层及ServiceImpl
4. 后端Controller层
5. 前端Vue页面(在views目录下创建新的目录和vue文件)
6. 前端路由配置
7. 前端API调用(在api目录下创建js文件)
注意:由于计划是基于指标新增的,因此在新增计划时,需要选择指标(可能是通过下拉框选择已有的指标)。
下面我们逐步进行:
### 1. 后端实体类
假设指标类(Indicator)包含以下字段:
- 指标ID(indicatorId)
- 指标名称(indicatorName)
- 其他字段...
计划类(Plan)包含以下字段:
- 计划ID(planId)
- 计划名称(planName)
- 关联的指标ID(indicatorId)
- 其他字段...
在`ruoyi-common`模块下的`com.ruoyi.common.core.domain.entity`包中创建这两个实体类(也可以根据业务放在其他包中,但通常放在entity包)。
**Indicator.java**:
```java
package com.ruoyi.common.core.domain.entity;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class Indicator extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 指标ID */
private Long indicatorId;
/** 指标名称 */
private String indicatorName;
// 其他字段...
}
```
**Plan.java**:
```java
package com.ruoyi.common.core.domain.entity;
import com.ruoyi.common.core.domain.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class Plan extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 计划ID */
private Long planId;
/** 计划名称 */
private String planName;
/** 关联的指标ID */
private Long indicatorId;
// 其他字段...
}
```
### 2. 后端Mapper层
在`ruoyi-system`模块的`com.ruoyi.system.mapper`包下创建`IndicatorMapper`和`PlanMapper`接口,并编写对应的XML文件(或在接口上使用注解方式,但复杂SQL建议用XML)。
**IndicatorMapper.java**:
```java
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.Indicator;
import java.util.List;
public interface IndicatorMapper {
public List<Indicator> selectIndicatorList(Indicator indicator);
// 其他方法...
}
```
**PlanMapper.java**:
```java
package com.ruoyi.system.mapper;
import com.ruoyi.common.core.domain.entity.Plan;
import java.util.List;
public interface PlanMapper {
public List<Plan> selectPlanList(Plan plan);
public Plan selectPlanById(Long planId);
public int insertPlan(Plan plan);
public int updatePlan(Plan plan);
public int deletePlanById(Long planId);
public int deletePlanByIds(Long[] planIds);
}
```
然后在`resources/mapper/system`下创建对应的XML文件,例如`PlanMapper.xml`。
**PlanMapper.xml**(部分):
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.PlanMapper">
<resultMap type="Plan" id="PlanResult">
<id property="planId" column="plan_id" />
<result property="planName" column="plan_name" />
<result property="indicatorId" column="indicator_id" />
<!-- 其他字段映射 -->
</resultMap>
<sql id="selectPlanVo">
select plan_id, plan_name, indicator_id from sys_plan
</sql>
<select id="selectPlanList" parameterType="Plan" resultMap="PlanResult">
<include refid="selectPlanVo"/>
<where>
<if test="planName != null and planName != ''"> AND plan_name like concat('%', #{planName}, '%') </if>
<if test="indicatorId != null"> AND indicator_id = #{indicatorId} </if>
</where>
</select>
<select id="selectPlanById" parameterType="Long" resultMap="PlanResult">
<include refid="selectPlanVo"/>
where plan_id = #{planId}
</select>
<insert id="insertPlan" parameterType="Plan" useGeneratedKeys="true" keyProperty="planId">
insert into sys_plan(
<if test="planName != null and planName != ''">plan_name,</if>
<if test="indicatorId != null">indicator_id,</if>
<!-- 其他字段 -->
)values(
<if test="planName != null and planName != ''">#{planName},</if>
<if test="indicatorId != null">#{indicatorId},</if>
<!-- 其他值 -->
)
</insert>
<!-- 更新和删除类似,此处省略 -->
</mapper>
```
注意:表名根据实际情况修改,这里假设为`sys_plan`。
### 3. Service层
创建`IPlanService`接口和实现类。
**IPlanService.java**:
```java
package com.ruoyi.system.service;
import com.ruoyi.common.core.domain.entity.Plan;
import java.util.List;
public interface IPlanService {
public List<Plan> selectPlanList(Plan plan);
public Plan selectPlanById(Long planId);
public int insertPlan(Plan plan);
public int updatePlan(Plan plan);
public int deletePlanById(Long planId);
public int deletePlanByIds(Long[] planIds);
}
```
**PlanServiceImpl.java**:
```java
package com.ruoyi.system.service.impl;
import com.ruoyi.common.core.domain.entity.Plan;
import com.ruoyi.system.mapper.PlanMapper;
import com.ruoyi.system.service.IPlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PlanServiceImpl implements IPlanService {
@Autowired
private PlanMapper planMapper;
@Override
public List<Plan> selectPlanList(Plan plan) {
return planMapper.selectPlanList(plan);
}
@Override
public Plan selectPlanById(Long planId) {
return planMapper.selectPlanById(planId);
}
@Override
public int insertPlan(Plan plan) {
return planMapper.insertPlan(plan);
}
@Override
public int updatePlan(Plan plan) {
return planMapper.updatePlan(plan);
}
@Override
public int deletePlanById(Long planId) {
return planMapper.deletePlanById(planId);
}
@Override
public int deletePlanByIds(Long[] planIds) {
return planMapper.deletePlanByIds(planIds);
}
}
```
### 4. Controller层
在`ruoyi-admin`模块的`com.ruoyi.web.controller.system`包下创建`PlanController`。
**PlanController.java**:
```java
package com.ruoyi.web.controller.system;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.core.domain.entity.Plan;
import com.ruoyi.system.service.IPlanService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/system/plan")
public class PlanController extends BaseController {
@Autowired
private IPlanService planService;
@PreAuthorize("@ss.hasPermi('system:plan:list')")
@GetMapping("/list")
public TableDataInfo list(Plan plan) {
startPage();
List<Plan> list = planService.selectPlanList(plan);
return getDataTable(list);
}
@PreAuthorize("@ss.hasPermi('system:plan:query')")
@GetMapping(value = "/{planId}")
public AjaxResult getInfo(@PathVariable Long planId) {
return success(planService.selectPlanById(planId));
}
@PreAuthorize("@ss.hasPermi('system:plan:add')")
@Log(title = "计划管理", businessType = BusinessType.INSERT)
@PostMapping
public AjaxResult add(@Validated @RequestBody Plan plan) {
return toAjax(planService.insertPlan(plan));
}
@PreAuthorize("@ss.hasPermi('system:plan:edit')")
@Log(title = "计划管理", businessType = BusinessType.UPDATE)
@PutMapping
public AjaxResult edit(@Validated @RequestBody Plan plan) {
return toAjax(planService.updatePlan(plan));
}
@PreAuthorize("@ss.hasPermi('system:plan:remove')")
@Log(title = "计划管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{planIds}")
public AjaxResult remove(@PathVariable Long[] planIds) {
return toAjax(planService.deletePlanByIds(planIds));
}
}
```
### 5. 前端Vue页面
在`ruoyi-ui`(前端项目)的`src/views`目录下创建`system/plan`目录,然后创建`index.vue`(列表页)和`detail.vue`(详情页,用于新增和编辑)等。
这里以列表页为例,展示计划列表,并提供新增、修改、删除按钮。
**src/views/system/plan/index.vue**:
```vue
<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 搜索区域 -->
<el-col :span="4">
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter.native="handleQuery" />
</el-col>
<el-col :span="4">
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd">新增</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="planList">
<el-table-column label="计划ID" prop="planId" width="120" />
<el-table-column label="计划名称" prop="planName" width="200" />
<el-table-column label="关联指标ID" prop="indicatorId" width="120" />
<!-- 其他列 -->
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<!-- 添加或修改计划对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="form.planName" placeholder="请输入计划名称" />
</el-form-item>
<el-form-item label="关联指标" prop="indicatorId">
<!-- 这里应该是一个下拉选择框,选择已有的指标 -->
<el-select v-model="form.indicatorId" placeholder="请选择指标">
<el-option v-for="item in indicatorOptions" :key="item.indicatorId" :label="item.indicatorName" :value="item.indicatorId"></el-option>
</el-select>
</el-form-item>
<!-- 其他表单项 -->
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listPlan, getPlan, addPlan, updatePlan, delPlan } from "@/api/system/plan";
import { listIndicator } from "@/api/system/indicator"; // 假设有获取指标列表的接口
export default {
name: "Plan",
data() {
return {
// 遮罩层
loading: true,
// 总条数
total: 0,
// 计划表格数据
planList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 指标选项
indicatorOptions: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
planName: undefined,
indicatorId: undefined
},
// 表单参数
form: {},
// 表单校验
rules: {
planName: [
{ required: true, message: "计划名称不能为空", trigger: "blur" }
],
indicatorId: [
{ required: true, message: "关联指标不能为空", trigger: "change" }
]
}
};
},
created() {
this.getList();
// 获取指标列表,用于下拉框
this.getIndicatorList();
},
methods: {
/** 查询计划列表 */
getList() {
this.loading = true;
listPlan(this.queryParams).then(response => {
this.planList = response.rows;
this.total = response.total;
this.loading = false;
});
},
// 获取指标列表
getIndicatorList() {
listIndicator().then(response => {
this.indicatorOptions = response.data;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
planId: undefined,
planName: undefined,
indicatorId: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加计划";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const planId = row.planId;
getPlan(planId).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改计划";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.planId != null) {
updatePlan(this.form).then(response => {
this.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
addPlan(this.form).then(response => {
this.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const planIds = row.planId;
this.$confirm('是否确认删除计划编号为"' + planIds + '"的数据项?', "警告", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(function() {
return delPlan(planIds);
}).then(() => {
this.getList();
this.msgSuccess("删除成功");
}).catch(() => {});
}
}
};
</script>
```
### 6. 前端路由配置
在`src/router/index.js`中添加路由:
```js
{
path: '/plan',
component: Layout,
hidden: false,
children: [
{
path: 'index',
component: () => import('@/views/system/plan/index'),
name: 'Plan',
meta: { title: '计划管理', icon: 'plan', roles: ['admin'] }
}
]
}
```
### 7. 前端API调用
在`src/api/system`目录下创建`plan.js`和`indicator.js`。
**plan.js**:
```js
import request from '@/utils/request'
// 查询计划列表
export function listPlan(query) {
return request({
url: '/system/plan/list',
method: 'get',
params: query
})
}
// 查询计划详细
export function getPlan(planId) {
return request({
url: '/system/plan/' + planId,
method: 'get'
})
}
// 新增计划
export function addPlan(data) {
return request({
url: '/system/plan',
method: 'post',
data: data
})
}
// 修改计划
export function updatePlan(data) {
return request({
url: '/system/plan',
method: 'put',
data: data
})
}
// 删除计划
export function delPlan(planId) {
return request({
url: '/system/plan/' + planId,
method: 'delete'
})
}
```
同样,创建`indicator.js`来获取指标列表(假设已有该接口)。
这样,我们就完成了在若依框架下新增计划管理模块的前后端分离实现。注意,以上代码是示例,实际开发中需要根据具体业务进行调整。
注意:在新增计划时,前端需要先获取指标列表(通过调用指标接口)并展示在下拉框中,用户选择指标后再提交。
以上步骤完成后,启动前后端项目,应该可以在菜单中看到“计划管理”,并实现增删改查功能。
§§相关问题§§
1. 若依框架中如何实现权限控制,比如新增的计划管理模块的权限?
2. 如果指标数据量很大,前端下拉框如何优化?
3. 计划管理中,除了关联指标ID,如何在前端展示指标名称?</think>在若依框架(RuoYi)前后端分离版本中实现指标(Indicator)和计划(Plan)实体类的增删改查功能,需按以下步骤进行:
---
### 一、后端实现(Spring Boot)
#### 1. 实体类创建
在`domain`包下创建实体类:
```java
// 指标实体 Indicator.java
@Data
public class Indicator {
private Long indicatorId; // 指标ID
private String indicatorName; // 指标名称
private String unit; // 单位
// 其他字段...
}
// 计划实体 Plan.java
@Data
public class Plan {
private Long planId; // 计划ID
private String planName; // 计划名称
private Long indicatorId; // 关联的指标ID
private Date startTime; // 开始时间
private Date endTime; // 结束时间
// 其他字段...
}
```
#### 2. Mapper层
在`mapper`包中创建接口:
```java
@Mapper
public interface PlanMapper {
List<Plan> selectPlanList(Plan plan);
int insertPlan(Plan plan);
int updatePlan(Plan plan);
int deletePlanById(Long planId);
}
// 指标Mapper同理(IndicatorMapper)
```
#### 3. Service层
```java
@Service
public class PlanServiceImpl implements PlanService {
@Autowired
private PlanMapper planMapper;
@Override
public List<Plan> selectPlanList(Plan plan) {
return planMapper.selectPlanList(plan);
}
@Override
public int insertPlan(Plan plan) {
return planMapper.insertPlan(plan);
}
// 其他方法实现...
}
```
#### 4. Controller层
```java
@RestController
@RequestMapping("/system/plan")
public class PlanController {
@Autowired
private PlanService planService;
// 查询计划列表
@GetMapping("/list")
public TableDataInfo list(Plan plan) {
startPage();
List<Plan> list = planService.selectPlanList(plan);
return getDataTable(list);
}
// 新增计划(需关联指标ID)
@PostMapping
public AjaxResult add(@RequestBody Plan plan) {
return toAjax(planService.insertPlan(plan));
}
// 其他方法:修改、删除...
}
```
---
### 二、前端实现(Vue.js)
#### 1. API接口(`src/api/system/plan.js`)
```javascript
import request from '@/utils/request'
// 查询计划列表
export function listPlan(query) {
return request({
url: '/system/plan/list',
method: 'get',
params: query
})
}
// 新增计划
export function addPlan(data) {
return request({
url: '/system/plan',
method: 'post',
data: data
})
}
// 其他接口:修改、删除...
```
#### 2. Vue页面(`src/views/system/plan/index.vue`)
```vue
<template>
<div class="app-container">
<!-- 搜索区域 -->
<el-form :model="queryParams">
<el-input v-model="queryParams.planName" placeholder="计划名称" />
<el-button @click="handleQuery">搜索</el-button>
</el-form>
<!-- 操作按钮 -->
<el-button type="primary" @click="handleAdd">新增计划</el-button>
<!-- 表格展示 -->
<el-table :data="planList">
<el-table-column prop="planName" label="计划名称" />
<el-table-column prop="indicatorName" label="关联指标" /> <!-- 需联表查询指标名称 -->
<el-table-column label="操作">
<template slot-scope="scope">
<el-button @click="handleEdit(scope.row)">编辑</el-button>
<el-button @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total>0" :total="total" />
<!-- 新增/编辑弹窗 -->
<el-dialog :title="title" :visible.sync="open">
<el-form ref="form" :model="form" :rules="rules">
<el-form-item label="计划名称" prop="planName">
<el-input v-model="form.planName" />
</el-form-item>
<el-form-item label="关联指标" prop="indicatorId">
<el-select v-model="form.indicatorId" placeholder="选择指标">
<el-option
v-for="item in indicatorList"
:key="item.indicatorId"
:label="item.indicatorName"
:value="item.indicatorId"
/>
</el-select>
</el-form-item>
</el-form>
<el-button @click="submitForm">提交</el-button>
</el-dialog>
</div>
</template>
<script>
import { listPlan, addPlan } from "@/api/system/plan";
import { listIndicator } from "@/api/system/indicator"; // 指标接口
export default {
data() {
return {
planList: [], // 计划列表
indicatorList: [], // 指标列表(用于下拉框)
queryParams: {}, // 查询参数
form: {}, // 表单数据
rules: { // 表单校验
planName: [{ required: true, message: "计划名称必填", trigger: "blur" }],
indicatorId: [{ required: true, message: "请选择指标", trigger: "change" }]
}
};
},
mounted() {
this.getList();
this.loadIndicators(); // 加载指标列表
},
methods: {
// 加载计划列表
getList() {
listPlan(this.queryParams).then(response => {
this.planList = response.rows;
});
},
// 加载指标列表(用于下拉框)
loadIndicators() {
listIndicator().then(res => {
this.indicatorList = res.rows;
});
},
// 新增计划弹窗
handleAdd() {
this.form = { indicatorId: null }; // 重置表单
this.open = true;
},
// 提交表单
submitForm() {
addPlan(this.form).then(() => {
this.$message.success("新增成功");
this.getList(); // 刷新列表
});
}
}
};
</script>
```
---
### 三、关键实现点
1. **数据关联**:
- 后端:在`PlanMapper.xml`中联表查询指标名称(如`LEFT JOIN sys_indicator ON plan.indicator_id = indicator.indicator_id`)。
- 前端:通过`loadIndicators()`加载指标列表到下拉框。
2. **权限控制**:
- 在Controller方法添加`@PreAuthorize("@ss.hasPermi('system:plan:add')")`实现按钮级权限。
3. **表单验证**:
- 前端使用`el-form`的`rules`属性校验必填字段。
- 后端在`Plan`实体类中添加`@NotBlank`等注解验证。
4. **异常处理**:
- 全局异常处理类`GlobalExceptionHandler`捕获并返回统一错误格式。
---
### 四、效果说明
1. **指标管理**:独立维护指标信息(名称、单位等)。
2. **计划管理**:
- 新增计划时通过下拉框选择关联指标。
- 表格中展示计划名称及关联的指标名称。
- 支持计划信息的编辑、删除和分页查询。
---
### 注意事项
1. 若依默认使用MyBatis,复杂查询需在`PlanMapper.xml`中编写SQL联表查询。
2. 前端页面路径需在`router/index.js`中配置路由。
3. 菜单权限需在后台“系统管理-菜单管理”中配置新增的`计划管理`菜单。
通过以上步骤,即可在若依框架中实现指标和计划的关联管理功能[^1][^2]。
阅读全文
相关推荐

















