新闻发布系统创建过程与源码分享

前言,适合人群

嘿,小伙伴,这是本人大二课程作业新闻发布系统的实现过程。如果您对前后端是如何交互感到困惑,或者想要初步学习vue3或后端的springboot框架的实践,又或是想要以此为基石完成属于自己课程作业或项目,那就上车,随我一起出发吧。(再次声明本文并非手把手教你的文章,需要您配合源码,以及我的半成品的图加以理解,因为这篇文章是我编写代码过程中成功经验给记录集合而已)
前提知识储备:springboot,mybatis sql(这部分不会讲)
主要分享:vue3

效果演示视频

视频here

Springboot+vue3新闻发布系统演示

项目启动引导教程

方便各位看的懂我是怎么做的,

vue3 vite项目启动

我使用的是Intellij idea,直接新建项目找到vite项目即可
![在这里插入图片描述](https://img-blog.csdnimg.cn/ba854858a5d445598ded0a385ed270e3.png在这里插入图片描述

兄弟们刚创建完毕定启动项目会出现vite的欢迎界面
我这里的news部分的位置应当直接在src目录下(不要学我,我这里只是为了管理多个页面,配置就挺麻烦的,vite本身就是做单页面的。通过改变组件来实现页面切换)在src下建立news下文件夹,给大家上传的源码部分只会包含news部分,vite-config.js请君自行上网查阅,或私聊

在这里插入图片描述

springboot项目启动

在这里插入图片描述
在这里插入图片描述
数据库依赖请自行选择
在这里插入图片描述

Day1 -2 新闻管理页面的基本搭建,后端搭建

问题1:请求跨域问题

在vite-config.js处加上对server的配置

//前后端分离,跨域配置,后端服务器地址为http://localhost:8080
  //想要访问http://localhost:8080/news来获取新闻数据,那么就需要配置路由重写
    // 重写规则如下:
    // 1. 以/api开头的请求,都会被代理到target配置
    // 2. 重写后的地址为:target(去掉/api) + /api(去掉/api)
    // 3. 例如:/api/news 重写后就是 http://localhost:8080 + /news
    // 4. 最终得到 http://localhost:8080/news
    // 5. 重写后的地址就会被代理到target配置
 server: {
        proxy: {
            '/local': {
                target: 'http://localhost:8080',
                rewrite: (path) => path.replace(/^\/local/, '')
            }
        }
    }

问题2 增删改中子组件与父组件的通信问题

需求描述; 点击编辑按钮/加号,弹出对话框(标题相应不同)填写表单,对话框组件封装在newsForm.vue中,父组件为news,为实现数据回显,以及是否弹出对话框,以下提供一个解决思路,当然也可以使用v-if
在这里插入图片描述

父组件通过ref获得子组件的实例,通过@向子组件传递自己的方法
在这里插入图片描述
子组件通过暴露自己的属性和方法,让父组件通过 const formInstance=ref()
之后使用formInstance可以访问到暴露的方法。
子组件通过使用emit访问到父组件的getNewsList方法。
在这里插入图片描述

问题3: element-plus怎么获取表格组件怎么获取行对象

需求描述:在点击编辑/删除时,我们需要知道究竟是修改了哪一行的数值

可以通过如下方法通过scope.row获得行对象
在这里插入图片描述

问题 4 前端如何发起请求

首先在httpRequest.js中配置请求基本路径以及拦截器(拦截器里面的处理可以暂时不写,在后面有业务需求在写)/local在这里要配合前面的跨域配置来写
在这里插入图片描述
然后在相应文件中按照这种方式发出请求,url地址与后端的接收的url要一致,传输与接收的参数,键值对中键名要与双方接收数据的属性名一致。传输对象时无须提前转换成json格式,object对象,hashmap对象都可以直接传入
在这里插入图片描述

Day3-4 后端全局异常处理函数,后端模糊,分页查询实现

package com.kinman.exception;

import com.kinman.pojo.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)//捕获所有异常
    public Result ex(Exception ex){
        ex.printStackTrace();
        return Result.error("对不起,操作失败,请联系管理员");
    }
}

在这里插入图片描述
这里给各位说明一下,分页查询的工作实际上是后端在做,利用sql语句实现的(当然可以借助插件)前端element-plus分页组件并不能直接与表格显示的内容相关联,仅仅是为了好看,用于通知前端或者后端pageSize,以及currentPage的数据,利用这我们可以使用前端或者后端进行数据剪枝。模糊查询使用like即可
前后端分页查询参考https://blog.csdn.net/ws6afa88/article/details/108955852

数据库异常原因总结

数据库服务未开启,库名,表明字段名出现了该数据库专有的关键字,例如postgresql中user与name。 博主因为这个原因浪费一个上午的时光和一天的好心情。
其余原因比较简单容易察觉的我就不再这里列出了

Day5-6 登录校验与请求拦截,token

问题一 怎么样登录校验

  1. 准备表单
  2. 准备规则
  3. 将规则绑定到表单上(到这一步只是提示无法防止用户直接提交表单)
  4. 获取表单对象
  5. 对表单对象进行整体校验
<script lang="ts" setup>
import {reactive, ref} from "vue";
import 'element-plus/theme-chalk/el-message.css'
import { ElMessage } from 'element-plus'
import {useRouter} from "vue-router";
import {useUserStore} from "@/pages/news/dataStore/userdata.js";
const userStore = useUserStore()
//reactive 用于将对象转化为响应式对象,在vue3中,ref只能用于基本类型,reactive可以用于对象
const form = reactive({
  username: '',
  password: '',
  agree: false
})

//校验规则,校验名必须与form中的属性名一致,按照产品经理的要求,用户名和密码都是必填项
const rules = {
  username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' },
    { min: 4, max: 14, message: '长度在 4 到 14 个字符', trigger: 'blur' }
  ],
  agree: [
    {
      validator: (rule, value, callback) => {
        if (!value) {
          callback(new Error('请勾选协议'))
        } else {
          callback()
        }
      }
    }
  ]
}
//获取form实例
const formRef = ref(null)
//获取路由实例
const router = useRouter()
const handleLogin = () => {

  //触发表单校验
  formRef.value.validate(async (valid) => {
    if (valid) {
      //校验成功
      console.log('校验成功')
      //todo 登录逻辑
      const {username, password} = form
      await userStore.getUserInfo({username, password})
      console.log(userStore.userInfo)
      //todo 登录成功后跳转到首页
      if (userStore.userInfo.code === 1) {
        //todo 提示登录成功
        ElMessage.success("登录成功")
        //todo 保存token
        //localStorage.setItem('token', res.data.token)
        //todo 跳转到首页
         await router.replace('/')
      }
    } else {
      //校验失败
      console.log('校验失败')
    }
  })
  console.log(form)
}

</script>
<template>
<div class="background">
<!--让elcard居中透明,上下也居中,不要遮挡背景-->
  <el-card class="loginCard" shadow="hover" style="width: 400px; margin: 200px auto 0;background: rgba(7,189,255,0.3)">
          <el-form ref="formRef" :model="form" :rules="rules" label-position="right" label-width="80px" status-icon>
<!--            prop指定校验名-->
            <el-form-item prop="username" >
<!--              #号的作用是将内容放到label中-->
              <template #label>
                <span style="color: #eeff00">用户名</span>
              </template>
              <el-input v-model="form.username" />
            </el-form-item>
            <el-form-item prop="password" >
              <template #label>
                <span style="color: #eeff00">密码</span>
              </template>
              <el-input v-model="form.password" />
            </el-form-item>
            <el-form-item prop="agree" label-width="22px">
              <el-checkbox size="large" v-model="form.agree">
               <i style="color: #00ffc4"> 我已同意隐私条款和服务条款</i>
              </el-checkbox>
            </el-form-item>
          </el-form>
    <!-- 登录按钮水平居中-->
    <div style="text-align: center">
      <el-button type="primary" @click="handleLogin">登录</el-button>
    </div>
  </el-card>
</div>
</template>
<style scoped>
.background{
  width: 100%;
  height: 100%;
  background: url("../../assets/loginbg.jpg");
  background-size:100% 100%;
  position: fixed;
  top: 0;
  left: 0;
}
.loginCard{
  margin-top: 200px;
}
</style>

token校验

  1. 向后端请求登录(一般校验到这就结束了,但因为是登录我们还需要之后保持token完成跳转功能)
  2. 后端返回token,配置拦截器
import com.kinman.interceptor.LoginCheckInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration//配置类
public class WebConfigure implements WebMvcConfigurer {
    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;
   @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

后端拦截器

package com.kinman.interceptor;

import com.kinman.pojo.Result;
import com.kinman.utils.JwtUtils;
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

@Slf4j
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override //目标资源方法运行前运行, 返回true: 放行, 放回false, 不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        //1.获取请求url。
        String url = req.getRequestURL().toString();
        log.info("请求的url: {}",url);

        //2.判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){
            log.info("登录操作, 放行...");
            return true;
        }

        //3.获取请求头中的令牌(token)。
        String jwt = req.getHeader("token");

        //4.判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){
            log.info("请求头token为空,返回未登录的信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            //手动写入响应体,返回给前端,前端解析json,如果是未登录,跳转到登录页面
            resp.getWriter().write(notLogin);
            return false;
        }

        //5.解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//jwt解析失败
            e.printStackTrace();
            log.info("解析令牌失败, 返回未登录错误信息");
            Result error = Result.error("NOT_LOGIN");
            //手动转换 对象--json --------> 阿里巴巴fastJSON
            String notLogin = JSONObject.toJSONString(error);
            resp.getWriter().write(notLogin);
            return false;
        }

        //6.放行。
        log.info("令牌合法, 放行");
        return true;
    }

    @Override //目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ...");
    }

    @Override //视图渲染完毕后运行, 最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

  1. 前端使用pinia或者使用localstorage进行持久化存储,使用方法见登录校验处的代码
import {defineStore} from "pinia";
import {ref} from "vue";
import {loginAPI} from "@/pages/news/apis/usersHandler"; // 1️⃣ import defineStore
export const useUserStore = defineStore("user", ()=>{ // 2️⃣ define a store
    const userInfo=ref({})// 3️⃣ add some state
    // 4️⃣ 定义一些actions
    const getUserInfo=async ({username,password})=>{
        userInfo.value=await loginAPI({username, password})
    }
    const clearUserInfo=()=>{
        userInfo.value={}
    }
    // 5️⃣ return the state and the actions
    return {
        userInfo,
        getUserInfo,
        clearUserInfo
    }
},{
    persist: true,
});

10.前端在请求拦截器请求处加上token字段,以及在后端响应处理后端传来token校验失败的信息

httpRequest.interceptors.request.use(config => {
    // 1.当发送网络请求时, 在页面中添加一个loading组件, 作为动画
    // 2.某些网络请求要求用户必须登录, 判断用户是否有token, 如果没有token跳转到login页面
    // 3.对请求的参数进行序列化(看服务器是否需要序列化)
        const userStore = useUserStore()
        const token=userStore.userInfo.data?userStore.userInfo.data.token:''
    if (token) {
        config.headers['token'] = token
    }
    //拦截管理员请求,验证用户权限,如果identity 为true放行,否则抛出错误
    if (config.url.includes("/ad")){
            if (!userStore.userInfo.data.user.identity){
                ElMessage({
                    type: 'warning',
                    message: '您没有权限访问该页面'
                })
                throw new Error('您没有权限访问该页面')
            }
    }
    return config;
}, error => {
        return Promise.reject(error);
    }
)
// 响应拦截器
httpRequest.interceptors.response.use(response => {
     //   console.log(response)
    //一般而言,只需要返回data即可
    if(response.data.code === 0){
        ElMessage({
            type: 'warning',
            message: response.data.msg
        })
        if (response.data.msg === 'NOT_LOGIN') {
            const userStore = useUserStore()
            userStore.clearUserInfo()
            router.push('/login')
        }
    }
        return response.data;
}, error => {
    return Promise.reject(error);
    }
)
export default httpRequest;  

前端项目地址

前端项目地址
现在已经暂时完结,感谢各位陪伴

  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
视展多媒体信息发布系统是利用显示屏将企业宣传、实时通知全方位展现出来的一种高清多媒体显 示技术。系统是将音视频、电视画面、图片、动画、文本、文档、网页、流媒体、数据库数据等组合成 一段段精彩的节目,并通过网络将制作好的节目实时的推送到分布在各地的媒体显示终端,从而将精彩 的画面、实时的信息资讯在各种指定场所全方位的完美展现在所需的群众眼前。     系统采用简单易用的B/S架构,基于网络平台采用分布式区域管理技术,可以有效整合各种信息发布 资源,实现随时随地远程制作、发布、管理及更新节目;系统采用专有传输协议,支持权限管理和节目 下载时的断点续传,在保证内容播出安全的同时,还能实现任意网络带宽下的高清图像质量;系统不仅 能播放几乎所有格式的音视频,同时还支持文字、文档、图片、网页等,并且充分融合了新闻、公告、 天气预报、银行汇率、牌价、服务资讯、滚动字幕、紧急通知、摄像采集、电视信号传输、现场直播等 流媒体信号、数据库系统对接等网络应用。使得播放内容不再仅仅局限于已有的固定素材,让播出的内 容更加灵活丰富、一目了然。__      集中控制管理:        系统采用B/S架构,无需安装客户端,即可在任意一台电脑上打开IE浏览器登录控制后台,对所 有终端进行任何操作管理。   任意分屏制作:         制作节目时可任意拉伸拖放视频、 图片、 FLASH、PPT的大小和区域。设置跑马灯字幕的字号 、字色、背景色、支持字幕向左、右、上、下滚动;系统自带数字日历时钟模块、天气预报模块、节目 模板库,支持自行设计制作模板;提供缩略图功能,所见即所得。   多媒体资源支持:          支持几乎所有的视频格式(rmvb、avi、mpeg、dat、mov、asf、mkv、wmv、3gp、vob等)音频 (mp3、wma、wav等)图片切换百叶窗等特效(jpg、gif、png等);支持流媒体电视,摄像采集等,支持 PPT、word/excel文档、RSS实时新闻、flash动画、动态网页等,支持在视频窗口上面添加动态FLASH的 LOGO。           支持与其它系统数据对接如:一卡通、门禁、排队叫号、触摸查询等;并支持电脑桌面实时 截取功能。   多种信息发布形式:           可任意定制某年、某月、某天或每天固定时段的发布任务;任务单编辑完全可视化,提供日 视图、周视图、月视图;节目排程逻辑清晰,方便操作,可针对不同的终端或分组制定不同的播放任务 。   系统的易用性:           支持终端的远程开关机,设置音量,支持下载状态的监控,以及终端实时画面的监控,精准 的终端文件清理工具,以及终端网络下载带宽的精准限制。   用户权限,终端分组:     多级用户分层管理,详细的用户权限分配,每个模块都可以分配给相应用户使用,终端的分组设置 ,终端数量多的情况下方便管理。   信息发布管理:     所有的用户操作全部记录日志,方便管理员对系统的管理维护。   节目分发:     支持多下载服务器同时分发节目,解决大并发量访问下载的瓶颈。 帐号密码:admin
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值