[Vue]做一个留言板界面

界面展示

[Vue]做一个留言板界面_第1张图片
[Vue]做一个留言板界面_第2张图片

  • 亮点:
    卡片的颜色与长度可以随机变化,自适应页面长度布局,用户可以点击like。

准备

  • MySQL
    一张message的表:
    [Vue]做一个留言板界面_第3张图片
    message:留言内容;
    username:由后端进行处理,如果提交的为空,那么设置名字为“匿名”;
    upTime:提交时间;
    likeNum:点赞数量,默认0。

  • 后端
    只需做一些简单的增删改查即可

    MessageMapper:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="cn.lzy.blog.Mapper.MessageMapper">
    
        <insert id="addMessage" useGeneratedKeys="true" keyProperty="id">
            insert into message set message = #{message},username=#{username},upTime=now()
        </insert>
        <update id="addLike">
            update message set likeNum = likeNum + 1 where id = #{id}
        </update>
        <delete id="deleteMessage">
            delete from message where id = #{id}
        </delete>
        <select id="selectAllMessage" resultType="cn.lzy.blog.Pojo.Message">
            select * from message order by upTime desc
        </select>
    </mapper>
    

    MessageController:

    @RestController
    @RequestMapping("/message")
    public class MessageController {
    
        @Autowired
        private MessageService messageService;
    
        @PostMapping("/add")
        public Response addMessage(@RequestBody Message message){
            Integer id = messageService.addMessage(message);
            if(id >0){
                return Response.Success("留言成功!",id);
            }
            return Response.Fail("留言失败");
        }
    
        @DeleteMapping("/del/{id}")
        public Response delMessage(@PathVariable Integer id){
            if(messageService.delMessage(id))
                return Response.Success("删除留言成功");
            return Response.Fail("删除留言失败");
        }
    
        @GetMapping("/all")
        public Response selMessage(@RequestParam @DefaultValue(value = "1") Integer pageNo,@RequestParam @DefaultValue(value = "10") Integer pageSize){
            PageInfo<Message> messagePageInfo = messageService.selectMessage(pageNo, pageSize);
            if(messagePageInfo != null){
                return Response.Success(messagePageInfo);
            }
            return Response.Fail("获取留言失败");
        }
    
        @PostMapping("/like")
        public Response addLike(@RequestBody JSONObject object){
            if(messageService.addLike(object.getInteger("id")))
                return Response.Success("success");
            return Response.Fail("fail");
        }
    }
    

    MessageService:

    @Service
    public class MessageService {
    
        @Autowired
        private MessageMapper messageMapper;
    
        public Integer addMessage(Message message){
            if(StringUtils.isEmpty(message.getUsername())){
                message.setUsername("匿名");
            }
            return messageMapper.addMessage(message);
        }
    
        public boolean delMessage(Integer id){
            if(messageMapper.deleteMessage(id) > 0)
                return true;
            return false;
        }
    
        public PageInfo<Message> selectMessage(Integer pageNo,Integer pageSize){
            PageHelper.startPage(pageNo,pageSize);
            return new PageInfo<>(messageMapper.selectAllMessage());
        }
    
        public boolean addLike(Integer id){
            if(messageMapper.addLike(id) > 0){
                return true;
            }
            return false;
        }
    }
    

前端

留言板的渲染主要是前端的工作,前后端分离下,后端只需分页查找返回数据,由前端进行渲染。

  • Card封装
    对每一条Card,我们进行封装,数据由父组件进行提供。在它加载的时候我们随机生成它的颜色,那么怎么做呢?
    设置颜色列表,加载时候产生随机数,随机选定一种颜色作为本卡片的颜色。
    每一个Card可以设置like,以及点击like的事件(提交post请求使like+1),为防止多次点击,可以设置标志位,点击后置为true,防止再次点击。

    <!--  -->
    <template>
        <el-card
          :style="'margin:0 10px 15px 0;background-color:' + color"
          shadow="hover"
        >
          <div>
            <h2 style="font-size: 100%;font-weight: normal;">{{messageData.upTime|dateFormat}}</h2>
          </div>
          <div>
            <p style="font-size: large;">{{messageData.message}}</p>
          </div>
          <div>
            <h3 class="username">来自:{{messageData.username}}</h3>
          </div>
          <div @click="like" style="float: right;">
            <el-link type="primary" :underline="false">
              <i class="el-icon-thumb"></i>
              ({{messageData.likeNum}})
            </el-link>
          </div>
        </el-card>
    </template>
    
    <script>
    import { dateFormat2 } from "@/utils/dateFormat";
    import {post} from '@/utils/http'
    export default {
      data() {
        return {
          colorList: ["#97cae6", "#cfc", "#FAC8C8", "#ffc", "#FAC8C8", "#ccf","#c9c","6cc","ff3","#9f6"],
          color: "",
          isLike: false,
        };
      },
      props: ["messageData"],
      mounted() {
        this.color = this.colorList[Math.floor(Math.random() * 10)];
      },
      methods: {
        like() {
          if (!this.isLike) {
            post("/message/like", { id: this.messageData.id }).then(res => {
              if (res.code == 200) {
                this.messageData.likeNum += 1;
                this.isLike = true;
              }
            });
          }
        },
        
      },
      filters: {
        dateFormat(val) {
          return dateFormat2(val);
        }
      }
    };
    </script>
    <style>
    .username {
      font-family: "Eater", cursive;
      color: rgba(33, 33, 33, 0.7);
    }
    </style>
    
  • 父组件调用
    加载的时候从后端获取Card数据,以及根据分页找到的本页数量,随机生成每一张Card的宽度width[i]。
    之后用v-for循环设置每一个卡片的生成,传递message给子组件。

    <div style="display: flex;justify-content: space-around;flex-wrap: wrap">
          <div
            v-for="(message,index) in messageData"
            :key="index"
            :style="'width:' + width[index] + '%'"
          >
            <Card :messageData="message"></Card>
          </div>
        </div>
    
  • 加载本页所有卡片的代码:

    <!--  -->
    <template>
      <div>
        <Add></Add>
        <div style="display: flex;justify-content: space-around;flex-wrap: wrap">
          <div
            v-for="(message,index) in messageData"
            :key="index"
            :style="'width:' + width[index] + '%'"
          >
            <Card :messageData="message"></Card>
          </div>
        </div>
        <div style="float:right">
          <el-pagination
            background
            layout="prev, pager, next"
            :total="total"
            @current-change="handleCurrentChange"
          ></el-pagination>
        </div>
      </div>
    </template>
    
    <script>
    import Add from "@/components/message/Add";
    import Card from "@/components/message/Card";
    import { get, post } from "@/utils/http";
    
    export default {
      data() {
        return {
          messageData: [],
          loading: false,
          total: 0,
          pageNo: 1,
          num: 0,
          width: []
        };
      },
      components: {
        Add,
        Card
      },
      mounted() {
        this.loadMessage();
      },
      methods: {
        loadMessage() {
          get("/message/all", {
            pageNo: this.pageNo,
            pageSize: 10
          }).then(res => {
            if (res.code == 200) {
              this.total = res.result.total;
              this.messageData = res.result.list;
              this.num = res.result.size;
              for (var i = 0; i < this.num; i++) {
                this.width[i] = this.randomNum(20, 50); //每个卡片宽度介于20%-50%之间
              }
              this.loading = false;
              window.scrollTo(0, 0);
            } else {
              this.$message.error(res.msg);
              this.loading = false;
            }
          });
        },
    
        handleCurrentChange(val) {
          this.pageNo = val;
          this.loadMessage();
        },
        randomNum(minNum, maxNum) { //产生一个介于minNum到maxNum的随机数
          switch (arguments.length) {
            case 1:
              return parseInt(Math.random() * minNum + 1, 10);
              break;
            case 2:
              return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
              break;
            default:
              return 0;
              break;
          }
        }
      }
    };
    </script>
    <style>
    </style>
    
  • 添加留言组件封装

    <!--  -->
    <template>
      <div>
        <el-input
          type="textarea"
          :autosize="{ minRows: 4, maxRows: 10}"
          placeholder="留下你对网站的意见或建议吧"
          v-model="message"
          style="width:60%;margin:18px 20% 0 20%"
        ></el-input>
        <div>
          <el-row :gutter="20">
            <el-col :span="12" :offset="5">
              <el-input
                placeholder="可以选择留下自己的昵称"
                v-model="username"
              ></el-input>
            </el-col>
            <el-col :span="7">
              <el-button @click="submit" type="primary" v-loading="loading" style="margin-bottom:18px">留言</el-button>
            </el-col>
          </el-row>
        </div>
      </div>
    </template>
    
    <script>
    import { post } from "@/utils/http";
    export default {
      data() {
        return {
          message: "",
          username: "",
          loading: false
        };
      },
      inject: ["reload"], //封装reload,用于刷新页面
      methods: {
        submit() {
          this.loading = true;
          if (this.message == "") {
            this.$message.error("你好像没有留言内容哦");
            this.loading = false;
            return;
          }
          var params = {
            username: this.username,
            message: this.message
          };
          post("/message/add", params).then(res => {
            if (res.code == 200) {
              this.$message({ type: "success", message: res.msg });
              this.reload();
            } else {
              this.$message.error(res.msg);
              this.username = "";
              this.message = "";
              this.loading = false;
            }
          });
        }
      }
    };
    </script>
    <style>
    </style>
    

结语

欢迎大家去我的网站留个言呀。

你可能感兴趣的:(Vue)