一、前言

在实际开发中,审核功能是一个非常常用的功能,例如管理后台的文章审核等等。本篇博文将介绍如何基于SpringBoot+Vue的前后端分离技术实现审核功能。

二、项目准备

本项目使用的技术栈为:

  • 前端:Vue+ElementUI
  • 后端:SpringBoot+MySQL

首先,你需要在本地搭建好Vue和SpringBoot的开发环境,建议使用最新版本。

三、数据库设计

本项目需要用到一个审核表,设计如下:

CREATE TABLE `audit` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `title` varchar(50) NOT NULL COMMENT '标题',
  `content` text NOT NULL COMMENT '内容',
  `status` tinyint(4) NOT NULL COMMENT '状态,0-待审核,1-审核通过,2-审核不通过',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='审核表';

四、后端实现

1. 数据库操作

首先,我们需要在后端设计一个操作数据库的Dao层来实现对审核表的增删改查操作。

@Repository
public interface AuditDao extends JpaRepository<Audit, Long> {
}

以上是使用Spring Data JPA实现的Dao层。

2. 业务逻辑

在Service层中,我们需要实现审核操作的相关逻辑:

@Service
public class AuditService {

    @Autowired
    private AuditDao auditDao;

    /**
     * 提交审核
     *
     * @param audit 审核实体类
     * @return 审核实体类
     */
    public Audit submitAudit(Audit audit) {
        audit.setStatus(Constant.AUDIT_STATUS_WAIT); // 默认状态为未审核
        audit.setCreateTime(LocalDateTime.now());
        audit.setUpdateTime(LocalDateTime.now());
        return auditDao.save(audit);
    }

    /**
     * 审核通过
     *
     * @param auditId 审核ID
     * @return 审核实体类
     */
    public Audit auditPass(Long auditId) {
        Audit audit = auditDao.findById(auditId).orElse(null);
        if (audit == null) {
            throw new RuntimeException("审核ID不存在!");
        }
        audit.setStatus(Constant.AUDIT_STATUS_PASS);
        audit.setUpdateTime(LocalDateTime.now());
        return auditDao.save(audit);
    }

    /**
     * 审核不通过
     *
     * @param auditId 审核ID
     * @return 审核实体类
     */
    public Audit auditReject(Long auditId) {
        Audit audit = auditDao.findById(auditId).orElse(null);
        if (audit == null) {
            throw new RuntimeException("审核ID不存在!");
        }
        audit.setStatus(Constant.AUDIT_STATUS_REJECT);
        audit.setUpdateTime(LocalDateTime.now());
        return auditDao.save(audit);
    }

    /**
     * 查询审核列表
     *
     * @param pageNum  分页页码
     * @param pageSize 分页大小
     * @return 审核列表数据
     */
    public Page<Audit> getAuditList(int pageNum, int pageSize) {
        return auditDao.findAll(PageRequest.of(pageNum - 1, pageSize, Sort.by(Sort.Order.desc("createTime"), Sort.Order.desc("id"))));
    }
}
3. 接口实现

在Controller层中,我们需要实现审核相关接口的实现:

@RestController
@RequestMapping("/audit")
public class AuditController {

    @Autowired
    private AuditService auditService;

    /**
     * 提交审核
     *
     * @param audit 审核实体类
     * @return 审核实体类
     */
    @PostMapping("/submitAudit")
    public Audit submitAudit(@RequestBody Audit audit) {
        return auditService.submitAudit(audit);
    }

    /**
     * 审核通过
     *
     * @param auditId 审核ID
     * @return 审核实体类
     */
    @PostMapping("/auditPass")
    public Audit auditPass(@RequestParam("auditId")Long auditId) {
        return auditService.auditPass(auditId);
    }

    /**
     * 审核不通过
     *
     * @param auditId 审核ID
     * @return 审核实体类
     */
    @PostMapping("/auditReject")
    public Audit auditReject(@RequestParam("auditId") Long auditId) {
        return auditService.auditReject(auditId);
    }

    /**
     * 查询审核列表
     *
     * @param pageNum  分页页码
     * @param pageSize 分页大小
     * @return 审核列表数据
     */
    @GetMapping("/getAuditList")
    public Page<Audit> getAuditList(@RequestParam("pageNum") int pageNum, @RequestParam("pageSize") int pageSize) {
        return auditService.getAuditList(pageNum, pageSize);
    }
}

这样我们的后端实现就完成了。

五、前端实现

1. 页面设计

在前端中,我们需要设计审核列表页面和审核详情页面。审核列表页面用来展示所有未审核通过的审核记录,审核详情页面则用来展示审核详情并提供审核通过和审核不通过的操作。

审核列表页面设计如下:

<template>
  <div>
    <el-button type="primary" class="mb-md" @click="dialogFormVisible = true">提交审核</el-button>
    <el-table :data="tableData" border stripe style="margin-left: -12px">
      <el-table-column prop="id" label="ID" width="70"></el-table-column>
      <el-table-column prop="title" label="标题" width="250"></el-table-column>
      <el-table-column prop="content" label="内容" width="580"></el-table-column>
      <el-table-column prop="createTime" label="创建时间" width="170"></el-table-column>
      <el-table-column prop="updateTime" label="更新时间" width="170"></el-table-column>
      <el-table-column
          prop="status"
          label="状态"
          width="90"
          :formatter="statusFormatter"
          :cell-style="{textAlign: 'center'}"
      ></el-table-column>
      <el-table-column
          label="操作"
          width="120"
          :cell-style="{textAlign: 'center'}"
          :render-header="renderHeader"
      >
        <template slot-scope="scope">
          <el-button type="primary" size="mini" v-if="scope.row.status === 0" @click="handlePass(scope.row.id)">通过</el-button>
          <el-button type="danger" size="mini" v-if="scope.row.status === 0" @click="handleReject(scope.row.id)">不通过</el-button>
          <el-button type="info" size="mini" :disabled="scope.row.status !== 1" @click="handleView(scope.row.id)">查看详情</el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-pagination
        background
        :page-size="pageSize"
        :total="total"
        layout="prev, pager, next"
        @current-change="getCurrentPage"
    >
    </el-pagination>

    <el-dialog
        title="提交审核"
        :visible.sync="dialogFormVisible"
        :close-on-click-modal="false"
        :before-close="handleDialogClose"
    >
      <el-form :model="form" :rules="rules" ref="form" label-width="120px">
        <el-form-item label="标题" prop="title">
          <el-input v-model="form.title"></el-input>
        </el-form-item>
        <el-form-item label="内容" prop="content">
          <el-input v-model="form.content" type="textarea"></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="submitAudit">提交</el-button>
          <el-button @click="dialogFormVisible = false">取消</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>

    <el-dialog
        title="审核详情"
        :visible.sync="viewDialogVisible"
        :close-on-click-modal="false"
        :before-close="handleDialogClose"
    >
      <div v-html="viewData.content"></div>
      <el-divider content-position="center" style="margin-top: 20px;">审核结果</el-divider>
      <el-alert
          class="mt-md"
          :title="viewData.status === 1 ? '审核通过' : '审核不通过'"
          :type="viewData.status === 1 ? 'success' : 'error'"
          :description="'审核时间:' + viewData.updateTime"
      ></el-alert>
      <div style="text-align: center">
        <el-button type="primary" @click="viewDialogVisible = false">关闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

审核详情页面设计如下:

<template>
  <div>
    <div v-html="viewData.content"></div>
    <el-divider content-position="center" style="margin-top: 20px;">审核结果</el-divider>
    <el-alert
        class="mt-md"
        :title="viewData.status === 1 ? '审核通过' : '审核不通过'"
        :type="viewData.status === 1 ? 'success' : 'error'"
        :description="'审核时间:' + viewData.updateTime"
    ></el-alert>
    <div style="text-align: center">
      <el-button type="primary" @click="handleBack">返回</el-button>
    </div>
  </div>
</template>
2. 数据交互

然后我们需要通过Axios实现前端向后端的数据交互。

首先,我们定义一个api.js文件:

import axios from 'axios'

axios.defaults.baseURL = '/api'

// 审核操作
export function auditOp(opType, auditId) {
  return axios.post(`/audit/${opType}`, {auditId})
}

// 获取审核列表
export function getAuditList(pageNum, pageSize) {
  return axios.get(`/audit/getAuditList?pageNum=${pageNum}&pageSize=${pageSize}`)
}

// 提交审核
export function submitAudit(audit) {
  return axios.post('/audit/submitAudit', audit)
}

// 获取审核详情
export function getAuditDetail(auditId) {
  return axios.get(`/audit/${auditId}`)
}

然后在页面中使用这些api:

import * as api from '@/api'

export default {
  name: 'AuditList',
  data() {
    return {
      tableData: [],
      total: 0,
      currentPage: 1,
      pageSize: 10,
      dialogFormVisible: false,
      viewDialogVisible: false,
      viewData: {},
      form: {
        title: '',
        content: ''
      },
      rules: {
        title: [
          {required: true, message: '请输入标题', trigger: 'blur'}
        ],
        content: [
          {required: true, message: '请输入内容', trigger: 'blur'}
        ]
      }
    }
  },
  mounted() {
    this.getAuditList(this.currentPage, this.pageSize)
  },
  methods: {
    // 获取审核列表
    getAuditList(pageNum, pageSize) {
      api.getAuditList(pageNum, pageSize)
        .then(res => {
          this.tableData = res.content
          this.total = res.totalElements
        })
    },
    // 审核通过
    handlePass(id) {
      this.$confirm('确定要通过该审核吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        api.auditOp('auditPass', id)
          .then(() => {
            this.$message({type: 'success', message: '审核通过'})
            this.getAuditList(this.currentPage, this.pageSize)
          })
      })
    },
    // 审核不通过
    handleReject(id) {
      this.$prompt('请输入不通过原因', '提示', {
        distinguishCancelAndClose: true,
        cancelButtonText: '取消',
        confirmButtonText: '确定'
      }).then(({value}) => {
        api.auditOp('auditReject', id)
          .then(() => {
            this.$message({type: 'success', message: '审核不通过'})
            this.getAuditList(this.currentPage, this.pageSize)
          })
      })
    },
    // 查看详情
    handleView(id) {
      api.getAuditDetail(id)
        .then(res => {
          this.viewData = res
          this.viewDialogVisible = true
        })
    },
    // 提交审核
    submitAudit() {
      this.$refs['form'].validate(valid => {
        if (valid) {
          api.submitAudit(this.form)
            .then(() => {
              this.$message({type: 'success', message: '提交审核成功'})
              this.dialogFormVisible = false
              this.getAuditList(this.currentPage, this.pageSize)
            })
        }
      })
    },
    // 获取当前分页页码
    getCurrentPage(page) {
      this.currentPage = page
      this.getAuditList(page, this.pageSize)
    },
    // 对话框关闭事件
    handleDialogClose(done) {
      this.$confirm('确定要关闭吗?')
        .then(() => {
          done()
        }).catch(() => {})
    },
    // 返回
    handleBack() {
      this.viewDialogVisible = false
    },
    // 状态格式化
    statusFormatter(row) {
      if (row.status === 0) {
        return '待审核'
      } else if (row.status === 1) {
        return '审核通过'
      } else {
        return '审核不通过'
      }
    },
    // 表头渲染
    renderHeader() {
      return '操作'
    }
  }
}

到此为止,我们的前端实现就完成了。

六、补充说明

1. 前后端分离架构

本项目采用的是前后端分离的架构模式,这个模式中前后端各自拥有自己的代码库,前端代码负责渲染页面和处理用户交互,后端代码负责处理数据逻辑和提供API接口。前端和后端通过API接口进行通信。这种架构模式的好处是可以很好地实现前后端分离,并且可以使开发效率更高。

2. 审核功能

审核功能是一个非常常用的功能,本文中实现了一个基本的审核功能,但实际开发中仍需要考虑更多的业务需求。例如:支持多种审核状态、支持审核流程配置、支持审核人员配置等等。

七、总结

本篇博客介绍了如何基于SpringBoot+Vue的前后端分离技术实现审核功能。在实际开发中,这种前后端分离的架构模式可以提高开发效率和开发质量,并且可以轻松实现业务扩展和维护。希望本篇博文可以帮助到大家。

Logo

前往低代码交流专区

更多推荐