Spring Boot + vue-element 开发个人博客项目实战教程(二十、登录日志、用户、分类管理页面开发)1

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Spring Boot + vue-element 开发个人博客项目实战教程(二十、登录日志、用户、分类管理页面开发)1

⭐ 作者简介:码上言



⭐ 代表教程:Spring Boot + vue-element 开发个人博客项目实战教程



⭐专栏内容:零基础学Java个人博客系统

项目部署视频

https://www.bilibili.com/video/BV1sg4y1A7Kv/?vd_source=dc7bf298d3c608d281c16239b3f5167b

文章目录

前言

有小伙伴开始催更了,最近我也在补充知识,我在有空的时候重新学习下Redis,有想要学的小伙伴可以来找我要笔记,后期看情况要不要分享出来,对于这个教程最基本的后端写完了,我们再搞搞前端争取快点结束掉,然后还是去重点搞java,目录我列了一些。

一、登录日志页面

上一篇我们只完成了操作日志的页面,我们首先完成剩下的登录日志。这个和我们操作日志的页面基本上差不多,我们的日志数据不支持删除,只能后台数据库进行删除。

1、设置api

src/api文件下找到operation.js文件,和操作日志一个,我们加一个请求的接口,这里和我们后台写的接口地址保持一致。

export function fetchLoginLogList(query) {
  return request({
    url: '/log/loginOperationLog/list',
    method: 'post',
    data: query
  })
}

由于我们一开始就设置了这个的路由,所以我们现在就直接去画页面。

2、页面

src/views/operation目录下,找到loginlog.vue,如果没有你就新建一个。以下是全部的代码,这里基本上和操作日志的页面一样,就改了一些展示的内容。

<template>
  <el-card class="box-card">
    <!-- 设置标题登录日志 -->
    <div slot="header" class="clearfix">
      <span>登录日志</span>
    </div>
    <el-table v-loading="listLoading" :data="list" fit highlight-current-row style="width: 98%; margin-top:30px;">
      <el-table-column align="center" label="ID" >
        <template slot-scope="scope">
          <span>{{ scope.row.id }}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="登录账号">
        <template slot-scope="scope">
          <span>{{ scope.row.loginName}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="登录IP">
        <template slot-scope="scope">
          <span>{{ scope.row.ipAddress}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="登录地点">
        <template slot-scope="scope">
          <span>{{ scope.row.loginLocation}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="浏览器类型">
        <template slot-scope="scope">
          <span>{{ scope.row.browserType}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="操作系统">
        <template slot-scope="scope">
          <span>{{ scope.row.os}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="登录状态">
        <template slot-scope="scope">
          <el-tag :type="tagType(scope.row.loginStatus)">
            {{ map[scope.row.loginStatus ] }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column align="center" label="操作系统">
        <template slot-scope="scope">
          <span>{{ scope.row.createTime}}</span>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      class="pagination-container"
      background
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="this.listQuery.pageNum"
      :page-size="this.listQuery.pageSize"
      :total="count"
      :page-sizes="[10, 20, 30]"
      layout="total, sizes, prev, pager, next, jumper"
    />
  </el-card>
</template>
<script>
import { fetchLoginLogList } from '@/api/operation'
export default {
  name: 'LoginOperationlog',
  created() {
    this.getList()
  },
   data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
      map: {
        0: '登录成功',
        1: '登录失败'
      }
    }
  },
  methods: {
    getList() {
      this.listLoading = true
      var body = this.listQuery;
      fetchLoginLogList({body}).then(response => {
        this.list = response.data.result
        this.count = response.data.totalSize
        this.listLoading = false
      })
    },
    handleSizeChange(pageSize) {
      this.listQuery.pageSize = pageSize
      this.getList()
    },
    handleCurrentChange(pageNum) {
      this.listQuery.pageNum = pageNum
      this.getList()
    }
  },
  computed: {
    tagType() {
      return function(type) {
        switch (type) {
          case 0:
            return "success";
          case 1:
            return "warning";
        }
      };
    }
  }
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
 .pagination-container {
    float: right;
    margin-top: 1.25rem;
    margin-bottom: 1.25rem;
 }
  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
</style>

这里只说一下登录的状态页面展示,我们后台返回前端的数据是登录成功状态为0,失败为1,所以我在前端判断了一下这个状态的展示,使得页面更加的美观。写到这里,我们的登录页面基本上完成了,此时日志中心也基本上完成。我们打开页面会看到这个时间有点小问题,后端返回过来的时间都带着T,这个影响我们数据的直观,所以我们在后台处理一下,只需要一个注解即可。

我们在实体类的时间上加上一下注解:

    /**
     * 创建时间
     */
    @JsonFormat(timezone = "GMT+8",pattern="yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

将操作日志和登录日志的时间都加上,重启项目即可。

二、路由管理

我想了一下接下来的开发流程,我先把整个的路由补充完整,让整个项目的框架搭起来,功能都列举出来,到时候我们直接在对应的页面中添加即可。

首先打开src/router目录,然后打开index.js

(1)用户管理路由

用户管理下面我们主要分两个子菜单,一个是用户列表,这个页面里包含了对用户的增删改查操作。另一个子菜单是个人简介,这里可以自由发挥,可以写写自己的介绍等。这里的icon图表可以去阿里的矢量图库中去下载。然后放到icons/svg目录下。

{
    path: '/user',
    component: Layout,
    redirect: '/user/list',
    name: 'User',
    meta: { title: '用户管理', icon: 'user' },
    children: [
      {
        path: 'list',
        name: 'UserList',
        component: () => import('@/views/user/list'),
        meta: { title: '用户列表', icon: 'user' }
      },
      {
        path: 'introduction',
        name: 'Introduction',
        component: () => import('@/views/user/introduction'),
        meta: { title: '个人介绍', icon: 'jieshao' }
      },
    ]
  },

然后我们在对应的目录下(/src/views)新建一个user文件夹,然后新建一个list.vueintroduction.vue文件。

先将以下代码填充进去,看下页面是否有数据即可。

<template>
    <div>
        用户管理
    </div>
</template>
<script>
export default {
  name: 'UserList',
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
</style>

(2)公告管理路由

接下来再创建公告的路由管理,和上面的用户一致,先写路由。

{
    path: '/notice',
    component: Layout,
    redirect: '/notice/list',
    name: 'Notice',
    meta: { title: '公告管理', icon: 'notice' },
    children: [
      {
        path: 'list',
        name: 'NoticeList',
        component: () => import('@/views/notice/list'),
        meta: { title: '公告列表', icon: 'notice' }
      },
      {
        path: 'add',
        name: 'AddNotice',
        component: () => import('@/views/notice/add'),
        meta: { title: '发布公告', icon: 'fabu' }
      },
    ]
 },

然后我们在对应的目录下(/src/views)新建一个notice文件夹,然后新建一个list.vueadd.vue文件。里面还是用上边用户的模板先填充即可。

(3)标签管理

 {
    path: '/tag',
    component: Layout,
    redirect: '/tag/list',
    name: 'Tag',
    meta: { title: '标签管理', icon: 'biaoqian' },
    children: [
      {
        path: 'list',
        name: 'TagList',
        component: () => import('@/views/tag/list'),
        meta: { title: '标签管理', icon: 'biaoqian' }
      },
    ]
  },

然后在新建一个标签管理的目录,参照上边的方式。新建一个tag文件夹,然后建一个list.vue即可。

(4)分类管理

  {
    path: '/categories',
    component: Layout,
    redirect: '/categories/list',
    name: 'Categories',
    meta: { title: '分类管理', icon: 'fenlei' },
    children: [
      {
        path: 'list',
        name: 'CategoriesList',
        component: () => import('@/views/categories/list'),
        meta: { title: '分类管理', icon: 'fenlei' }
      },
    ]
  },

然后再新建一个tag文件夹,然后建一个list.vue即可。

(5)文章管理

{
    path: '/articles',
    component: Layout,
    redirect: '/articles/list',
    name: 'Articles',
    meta: { title: '文章管理', icon: 'wz' },
    children: [
      {
        path: 'list',
        name: 'ArticlesList',
        component: () => import('@/views/articles/list'),
        meta: { title: '文章列表', icon: 'wenzhang' }
      },
      {
        path: 'add',
        name: 'Addrticles',
        component: () => import('@/views/articles/add'),
        meta: { title: '发布文章', icon: 'fabu' }
      },
    ]
  },

然后再新建一个articles文件夹,然后建一个list.vueadd.vue即可。

好啦,我们将左侧的导航功能基本上全部搭建好了,有点系统的样子了。

三、分类管理

首先我们上边已经创建了路由的管理,所以我们页面也已经创建好了,接下来我们来创建一下api接口对接。

1、创建api

新建一个category.js文件,主要是最基础的增删改查接口。

import request from '@/utils/request'
export function categoryList(query) {
    return request({
      url: '/category/list',
      method: 'post',
      data: query
    })
}
export function addCategory(data) {
    return request({
      url: '/category/create',
      method: 'post',
      data
    })
}
export function updateCategory(data) {
    return request({
      url: '/category/update',
      method: 'post',
      data
    })
}
export function deleteCategory(id) {
  return request({
    url: '/category/delete',
    method: 'post',
    params: { id }
  })
}

2、分类列表

还是老规矩,先画页面,看了一下后端代码,我们还是采用的分页展示,所以这里面还要写一个分页,和用户的基本上一致。

<template>
  <el-card class="box-card">
    <el-table v-loading="listLoading" :data="list" fit highlight-current-row style="width: 98%; margin-top:30px;">
      <el-table-column align="center" label="ID" >
        <template slot-scope="scope">
          <span>{{ scope.row.categoryId }}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="分类名称">
        <template slot-scope="scope">
          <span>{{ scope.row.categoryName}}</span>
        </template>
      </el-table-column>
       <el-table-column align="center" label="创建时间">
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.createTime}}</span>
        </template>
      </el-table-column>
       <el-table-column align="center" label="更新时间">
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.updateTime}}</span>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      class="pagination-container"
      background
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="this.listQuery.pageNum"
      :page-size="this.listQuery.pageSize"
      :total="count"
      :page-sizes="[10, 20, 30]"
      layout="total, sizes, prev, pager, next, jumper"
    />
  </el-card>
</template>

引入接口:

import { categoryList} from '@/api/category'

然后接收接口传来的数据

export default {
  name: 'CategoriesList',
  created() {
    this.getList()
  },
  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
    }
  },
  methods: {
    getList() {
      this.listLoading = true
      var body = this.listQuery;
      categoryList({body}).then(response => {
        this.list = response.data.result
        this.count = response.data.totalSize
        this.listLoading = false
      })
    },
    handleSizeChange(pageSize) {
      this.listQuery.pageSize = pageSize
      this.getList()
    },
    handleCurrentChange(pageNum) {
      this.listQuery.pageNum = pageNum
      this.getList()
    }
  }
}

然后再修改一下样式:

<style rel="stylesheet/scss" lang="scss" scoped>
 .pagination-container {
    float: right;
    margin-top: 1.25rem;
    margin-bottom: 1.25rem;
 }
  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
</style>

这里面我们将分页请求的参数外边又包了一层body,所以,后端如果没有修改的话,修改一下

    @ApiOperation(value = "分类列表")
    @PostMapping("list")
    public JsonResult<Object> listPage(@RequestBody @Valid PageRequestApi<PageRequest> pageRequest) {
        List<Category> categoryList = categoryService.getCategoryPage(pageRequest.getBody());
        PageInfo pageInfo = new PageInfo(categoryList);
        PageResult pageResult = PageUtil.getPageResult(pageRequest.getBody(), pageInfo);
        return JsonResult.success(pageResult);
    }

都写完之后,可以启动项目,查看是否有数据展示出来。


3、添加和修改

这两个放一起来说,因为我们这里面的功能比较简单,所以我就用了一个弹出框来作为用户输入的信息,根据id来判断是增加的还是修改的,大家应该知道,添加的数据此时还没有id值,所以用id作为区分。

接口已经写好,现在我们将接口的两个方法引进来。

  import { categoryList, addCategory, updateCategory } from '@/api/category'

然后我们新建一个添加的按钮和一个编辑的按钮,这个添加的按钮我们就放到了列表的左上角即可,一般的系统都是这样设计的,编辑的则放在列表中操作,再加一个操作列,主要放编辑和删除两个功能,跟随每一条数据操作。

 <el-button type="primary" size="small" icon="el-icon-plus" @click="transformation(null)">新增分类</el-button>

这里我们使用了一个点击事件,方法为transformation(),我们等一下再创建。

然后再加一个操作的列。这里的编辑也是用了和添加一样的点击事件。

<el-table-column align="center" label="操作" width="180">
     <template slot-scope="scope">
        <el-button type="primary" size="mini" icon="el-icon-edit" @click="transformation(scope.row)">编辑</el-button>
     </template>
</el-table-column>

添加完之后,然后我们去写transformation()方法。

下面这个方法就是添加和修改都调用了,这个主要的功能是什么呢?,首先我们看到,方法带了一个参数,大家去上边添加和修改中看一下调用这个传的参数有什么不同。大家会发现添加的是传了一个null,修改的则是将展示的这一行的数据传了进来。讲到这里大家,大家再看一下代码,哦哦。原来是这样区分的,修改的时候我们就会把数据给带过来,添加的话则为空。这个也主要是为了弹窗准备的,共用了一个弹窗。

transformation(category) {
      if (category != null) {
        this.categoryForm = JSON.parse(JSON.stringify(category));
        this.$refs.categoryTitle.innerHTML = "修改分类";
      } else {
        this.categoryForm.categoryId = null;
        this.categoryForm.categoryName = "";
        this.$refs.categoryTitle.innerHTML = "添加分类";
      }
      this.addOrupdateDialogVisible = true;
 },

最下面可能会看到还有一句话:this.addOrupdateDialogVisible = true;这个主要是控制弹窗是否显示,我们需要再return中定义一下。categoryForm也需要定义。

return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
      addOrupdateDialogVisible: false,
      categoryForm: {
        categoryId: null,
        categoryName: ""
      },
    }

接下来我们写添加或者修改的对话框。

我先列出部分功能讲解,最后我会将完整的代码附上。

 <!-- 添加或修改分类对话框 -->
    <el-dialog :visible.sync="addOrupdateDialogVisible" width="30%">
      <div class="dialog-title-container" slot="title" ref="categoryTitle" />
      <el-form label-width="100px" size="medium" :model="categoryForm">
        <el-form-item label="分类名称:">
          <el-input v-model="categoryForm.categoryName" style="width:220px" />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="addOrupdateDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="addCategory">
          确 定
        </el-button>
      </div>
    </el-dialog>

我们来分析一下,<el-dialog :visible.sync="addOrupdateDialogVisible" width="30%">这个则是控制我们对话框弹不弹出的控制,接下来categoryTitle这个大家可以在transformation()方法中找到对它的赋值,其实就是左上角的标题。然后还有就是取消控制和确定,点击确定则会触发addCategory事件,对分类的修改或添加。

然后接下来我们就去写addCategory方法。

我从上到下来说一下流程:

首先我们要判断一下页面上的输入框分类名称是不是为空,为空的话则返回错误信息,紧接着然后获取表单的信息,如果id为null则表示添加的操作,这时走添加的接口addCategory(),否则的话会走updateCategory()方法。

最后再设置对话框为false,基本上的操作流程就这些,都是一般的逻辑。

addCategory() {
      if (this.categoryForm.categoryName.trim() == "") {
        this.$message.error("分类名不能为空");
        return false;
      }
      var body = this.categoryForm;
      if(body.categoryId == null){
        addCategory(body).then(response => {
          this.$message({
            type: 'success',
            message: '添加分类成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      } else {
        updateCategory(body).then(response => {
          this.$message({
            type: 'success',
            message: '修改分类成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      }

下面再把删除功能做了,再一起测试。删除功能比较简单点,我们先添加一个删除按钮再操作列中。

4、删除

<el-button type="danger" size="small" icon="el-icon-delete" @click="deleteCategoryById(scope.row.categoryId)">删除</el-button>

然后调用了删除的方法,接下来去完成删除的方法。这个也是很简单的,我们只要传入一个id给后端,然后后端根据id去删除即可,调用接口。

  import { categoryList, addCategory, updateCategory, deleteCategory } from '@/api/category'
deleteCategoryById (id) {
      this.$confirm('此操作将永久删除该分类, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteCategory(id).then(response => {
           this.$message({
            type: 'success',
            message: '删除成功!'
          })
           this.getList()
        }).catch(() => {
          console.log('error')
        })
      }).catch(() => {
         this.$message({
            type: 'error',
            message: '你已经取消删除该分类!'
          })
      })
    },

写完这个,因为我们传入的参数格式不一致,我们要修改后端接口代码:

    @ApiOperation(value = "删除分类")
    @PostMapping("/delete")
    @OperationLogSys(desc = "删除分类", operationType = OperationType.DELETE)
    public JsonResult<Object> categoryDelete(@RequestParam(value = "id") int id) {
        categoryService.deleteCategory(id);
        return JsonResult.success();
    }

好啦,到这里在展示、添加、修改、刪除的功能基本完成,我们测试一下这几个功能。运行一下前后端项目。

添加一条数据:修改分类:删除分类:分类完整代码:

<template>
  <el-card class="box-card">
    <el-button type="primary" size="small" icon="el-icon-plus" @click="transformation(null)">新增分类</el-button>
    <el-table v-loading="listLoading" :data="list" fit highlight-current-row style="width: 98%; margin-top:30px;">
      <el-table-column align="center" label="ID" >
        <template slot-scope="scope">
          <span>{{ scope.row.categoryId }}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="分类名称">
        <template slot-scope="scope">
          <span>{{ scope.row.categoryName}}</span>
        </template>
      </el-table-column>
       <el-table-column align="center" label="创建时间">
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.createTime}}</span>
        </template>
      </el-table-column>
       <el-table-column align="center" label="更新时间">
        <template slot-scope="scope">
          <i class="el-icon-time" style="margin-right:5px" />
          <span>{{ scope.row.updateTime}}</span>
        </template>
      </el-table-column>
      <el-table-column align="center" label="操作" width="180">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" icon="el-icon-edit" @click="transformation(scope.row)">编辑</el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" @click="deleteCategoryById(scope.row.categoryId)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <el-pagination
      class="pagination-container"
      background
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="this.listQuery.pageNum"
      :page-size="this.listQuery.pageSize"
      :total="count"
      :page-sizes="[10, 20, 30]"
      layout="total, sizes, prev, pager, next, jumper"
    />
    <!-- 添加或修改分类对话框 -->
    <el-dialog :visible.sync="addOrupdateDialogVisible" width="30%">
      <div class="dialog-title-container" slot="title" ref="categoryTitle" />
      <el-form label-width="100px" size="medium" :model="categoryForm">
        <el-form-item label="分类名称:">
          <el-input v-model="categoryForm.categoryName" style="width:220px" />
        </el-form-item>
      </el-form>
      <div slot="footer">
        <el-button @click="addOrupdateDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="addCategory">
          确 定
        </el-button>
      </div>
    </el-dialog>
  </el-card>
</template>
<script>
  import { categoryList, addCategory, updateCategory, deleteCategory } from '@/api/category'
export default {
  name: 'CategoriesList',
  created() {
    this.getList()
  },
  data() {
    return {
      list: null,
      listLoading: true,
      count: 0,
      listQuery: {
        pageNum: 1,
        pageSize: 10
      },
      addOrupdateDialogVisible: false,
      categoryForm: {
        categoryId: null,
        categoryName: ""
      },
    }
  },
  methods: {
    getList() {
      this.listLoading = true
      var body = this.listQuery;
      categoryList({body}).then(response => {
        this.list = response.data.result
        this.count = response.data.totalSize
        this.listLoading = false
      })
    },
    transformation(category) {
      if (category != null) {
        this.categoryForm = JSON.parse(JSON.stringify(category));
        this.$refs.categoryTitle.innerHTML = "修改分类";
      } else {
        this.categoryForm.categoryId = null;
        this.categoryForm.categoryName = "";
        this.$refs.categoryTitle.innerHTML = "添加分类";
      }
      this.addOrupdateDialogVisible = true;
    },
    addCategory() {
      if (this.categoryForm.categoryName.trim() == "") {
        this.$message.error("分类名不能为空");
        return false;
      }
      var body = this.categoryForm;
      if(body.categoryId == null){
        addCategory(body).then(response => {
          this.$message({
            type: 'success',
            message: '添加分类成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      } else {
        updateCategory(body).then(response => {
          this.$message({
            type: 'success',
            message: '修改分类成功!'
          })
          this.getList()
        }).catch(() => {
          console.log('error')
        })
      }
      this.addOrupdateDialogVisible = false;
    },
    deleteCategoryById (id) {
      this.$confirm('此操作将永久删除该分类, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        deleteCategory(id).then(response => {
           this.$message({
            type: 'success',
            message: '删除成功!'
          })
           this.getList()
        }).catch(() => {
          console.log('error')
        })
      }).catch(() => {
         this.$message({
            type: 'error',
            message: '你已经取消删除该分类!'
          })
      })
    },
    handleSizeChange(pageSize) {
      this.listQuery.pageSize = pageSize
      this.getList()
    },
    handleCurrentChange(pageNum) {
      this.listQuery.pageNum = pageNum
      this.getList()
    }
  }
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
 .pagination-container {
    float: right;
    margin-top: 1.25rem;
    margin-bottom: 1.25rem;
 }
  .box-card {
    width: 98%;
    margin: 1%;
  }
  .clearfix:before,
  .clearfix:after {
    display: table;
    content: "";
  }
  .clearfix:after {
    clear: both
  }
  .clearfix span {
    font-weight: 600;
  }
</style>
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
21天前
|
监控 Java 应用服务中间件
SpringBoot是如何简化Spring开发的,以及SpringBoot的特性以及源码分析
Spring Boot 通过简化配置、自动配置和嵌入式服务器等特性,大大简化了 Spring 应用的开发过程。它通过提供一系列 `starter` 依赖和开箱即用的默认配置,使开发者能够更专注于业务逻辑而非繁琐的配置。Spring Boot 的自动配置机制和强大的 Actuator 功能进一步提升了开发效率和应用的可维护性。通过对其源码的分析,可以更深入地理解其内部工作机制,从而更好地利用其特性进行开发。
42 6
|
28天前
|
Java 应用服务中间件 API
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
38 5
|
28天前
|
前端开发 Java 数据库连接
Java后端开发-使用springboot进行Mybatis连接数据库步骤
本文介绍了使用Java和IDEA进行数据库操作的详细步骤,涵盖从数据库准备到测试类编写及运行的全过程。主要内容包括: 1. **数据库准备**:创建数据库和表。 2. **查询数据库**:验证数据库是否可用。 3. **IDEA代码配置**:构建实体类并配置数据库连接。 4. **测试类编写**:编写并运行测试类以确保一切正常。
52 2
|
1月前
|
人工智能 Java API
阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手
本次分享的主题是阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手,由阿里云两位工程师分享。
阿里云工程师跟通义灵码结伴编程, 用Spring AI Alibaba来开发 AI 答疑助手
|
2月前
|
人工智能 前端开发 Java
Spring AI Alibaba + 通义千问,开发AI应用如此简单!!!
本文介绍了如何使用Spring AI Alibaba开发一个简单的AI对话应用。通过引入`spring-ai-alibaba-starter`依赖和配置API密钥,结合Spring Boot项目,只需几行代码即可实现与AI模型的交互。具体步骤包括创建Spring Boot项目、编写Controller处理对话请求以及前端页面展示对话内容。此外,文章还介绍了如何通过添加对话记忆功能,使AI能够理解上下文并进行连贯对话。最后,总结了Spring AI为Java开发者带来的便利,简化了AI应用的开发流程。
783 0
|
1月前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
前端开发 Java 开发者
这款免费 IDEA 插件让你开发 Spring 程序更简单
Feign-Helper 是一款支持 Spring 框架的 IDEA 免费插件,提供 URL 快速搜索、Spring Web Controller 路径一键复制及 Feign 与 Controller 接口互相导航等功能,极大提升了开发效率。
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
200 17
Spring Boot 两种部署到服务器的方式
|
27天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
59 17
springboot自动配置原理
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
80 11

热门文章

最新文章