【2020-09-23】day06:使用v-for实现简单评论区效果

个人知识碎片的体系化理解与整理,缓慢更新,理解不到位与错漏之处望见谅与指正。

day06摘要思想:vue中v-for指令可对数组进行遍历并根据每个数组元素单独进行组件渲染,配合基础后台增删改查服务可实现动态评论功能;页面需要使用iView的Grid栅格布局及Card组件等

开发环境:WebStorm 2019.1

 

1.评论表设计与后台代码生成

表设计如下:

CREATE TABLE `zd_course_comment` (
	`id` BIGINT(20) NOT NULL,
	`course_id` BIGINT(20) NOT NULL COMMENT '课程id',
	`comment_context` VARCHAR(200) NULL DEFAULT '' COMMENT '评论内容',
	`comment_name` VARCHAR(50) NULL DEFAULT '' COMMENT '评论者',
	`comment_icon` VARCHAR(200) NULL DEFAULT '' COMMENT '评论者头像',
	`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	PRIMARY KEY (`id`)
)
COMMENT='课程评论表'
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;

后台Controller层对外接口代码生成:

    /**
     * 根据Id 查询
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/getByPrimaryKey")
    public Result getByPrimaryKey(@RequestParam Long id) {
        log.info("根据Id查询 CourseComment,参数= {} ", JSONObject.toJSONString(id));
        return Result.ok(this.courseCommentService.getByPrimaryKey(id));
    }

    /**
     * 分页查询
     *
     * @param queryMap
     * @return
     */
    @GetMapping(value = "/findPage")
    public Result findPage(@RequestParam Map<String, Object> queryMap) {
        log.info("查询 courseComment 分页列表,参数= {} ", JSONObject.toJSONString(queryMap));
        PageUtils record = this.courseCommentService.findPage(queryMap);
        return Result.ok(record);
    }

    /**
     * 列表查询
     *
     * @param queryMap
     * @return
     */
    @GetMapping(value = "/list")
    public Result list(@RequestParam Map<String, Object> queryMap) {
        log.info("查询 courseComment 列表,参数= {} ", JSONObject.toJSONString(queryMap));
        List<CourseComment> record = this.courseCommentService.list(queryMap);
        return Result.ok(record);
    }

    /**
     * 新增数据
     *
     * @param entity
     * @return 新增结果
     */
    @PostMapping(value = "/save")
    public Result save(@RequestBody @Validated CourseComment entity, BindingResult bindingResult) {
        log.info("新增 courseComment,参数= {} ", JSONObject.toJSONString(entity));
        if (bindingResult.hasErrors()) {
            log.error("新增 courseComment 参数不正确,异常={}", bindingResult.getFieldError().getDefaultMessage());
            return Result.error(ResultEnum.ERROR_400.getCode(), bindingResult.getFieldError().getDefaultMessage());
        }
        this.courseCommentService.save(entity);
        return Result.ok();
    }

    /**
     * 更新
     *
     * @param entity
     * @return
     */
    @PostMapping(value = "/update")
    public Result updateById(@RequestBody @Validated CourseComment entity, BindingResult bindingResult) {
        log.info("更新 CourseComment,参数= {} ", JSONObject.toJSONString(entity));
        if (bindingResult.hasErrors()) {
            log.error("更新 courseComment 参数不正确,异常={}", bindingResult.getFieldError().getDefaultMessage());
            return Result.error(ResultEnum.ERROR_400.getCode(), bindingResult.getFieldError().getDefaultMessage());
        }
        this.courseCommentService.updateById(entity);
        return Result.ok();
    }

    /**
     * 删除
     *
     * @param ids
     * @return
     */
    @PostMapping(value = "/delete")
    public Result deleteById(@RequestBody List<Long> ids) {
        log.info("删除 CourseComment,参数= {} ", JSONObject.toJSONString(ids));
        this.courseCommentService.deleteById(ids);
        return Result.ok();
    }

 

2.前端组件层布局搭建

核心代码如下,其中Row/Col、Card、Menu、Input、Page等均为iView的现成组件,建议参考官方文档使用:

            <Row>
                <Col span="18">
                    <video-player id="videoPlayer" class="video-player vjs-custom-skin" ref="videoPlayer" :playsinline="true" :options="playerOptions"></video-player>
                    <Card id="otherInfoCard" style="height:600px">
                        <h1 class="cardTitle">{{classData.courseName}}</h1>
                        <Divider />
                    </Card>
                </Col>
                <Col span="6">
                    <Card style="overflow:auto" id="infoCard">
                        <div class="videoMenu">
                            <Menu @on-select="handleClick" mode="horizontal" active-name="1" >
                                <MenuItem name="1" >课程详情</MenuItem>
                                <MenuItem name="2">评论({{commentData.total}})</MenuItem>
                            </Menu>
                        </div>
                        <div v-if="menuName === '1'">
                            <!--课程详情-->
                            <p style="line-height:40px;margin-top:15px">课程名称:{{classData.courseName}}</p>
                            <p style="line-height:40px">学段:
                                <span v-if="classData.stageCode === 1">小学</span>
                                <span v-if="classData.stageCode === 2">初中</span>
                                <span v-if="classData.stageCode === 3">高中</span>
                            </p>
                            <p style="line-height:40px">年级:{{classData.gradeName}}</p>
                            <p style="line-height:40px">学科:{{classData.subjectName}}</p>
                            <p style="line-height:40px">发布时间:{{classData.createTime}}</p>
                        </div>
                        <Card shadow v-if="menuName === '2'" class="commentDiv" id="commentCard">
                            <div v-for="item in commentData.data" :key="item.id">
                                <!--评论-->
                                <Row>
                                    <Col span="5">
                                        <img :src="item.commentIcon" style="width:50px;height:50px;margin-top:10%"/>
                                    </Col>
                                    <Col span="19">
                                        <Row>
                                            <Col span="8">
                                                <h4 style="line-height:30px">{{item.commentName}}</h4>
                                            </Col>
                                            <Col span="16" align="right">
                                                <p style="line-height:30px;font-size:12px">{{item.createTime}}</p>
                                            </Col>
                                        </Row>
                                        <p style="line-height:30px;font-size:14px;word-break:break-all;">
                                            {{item.commentContext}}
                                        </p>
                                    </Col>
                                </Row>
                                <Divider v-if="(commentData.data.indexOf(item) !== commentData.data.length - 1) || commentData.totalPage > 1"/>
                            </div>
                            <Page align="right" v-if="commentData.totalPage > 1" size="small" :total="commentData.total" @on-change="pageChange" :current.sync="commentPageNum" :page-size="commentPageSize" @on-page-size-change="pageSizeChange"></Page>
                        </Card>
                        <div v-if="menuName === '2'" style="position:absolute;bottom:2%;width:90%">
                            <Input v-model="commentModel" :maxlength="Number(200)" placeholder="请输入评论,不超过200字...">
                                <Button slot="append" @click="commentSubmit">提交</Button>
                            </Input>
                        </div>
                    </Card>
                </Col>
            </Row>

 

3.原生js方式进行高度自适应

评论区卡片组件的高度根据左侧组件的高度进行动态修改,需要监听onresize事件并使用document.getElementById获取元素并动态修改其height属性:

        mounted() {
            //第一次进入时未触发onresize事件,手动调用一次
            let infoCard = document.getElementById("infoCard");
            let videoPlayer = document.getElementById("videoPlayer");
            let otherInfoCard = document.getElementById("otherInfoCard");
            infoCard.style.height = String(videoPlayer.offsetHeight + otherInfoCard.offsetHeight) + "px";
            //监听onresize事件
            window.onresize = () => {
                return (() => {
                    let infoCard = document.getElementById("infoCard");
                    let videoPlayer = document.getElementById("videoPlayer");
                    let otherInfoCard = document.getElementById("otherInfoCard");
                    infoCard.style.height = String(videoPlayer.offsetHeight + otherInfoCard.offsetHeight) + "px";
                    let commentCard = document.getElementById("commentCard");
                    if(commentCard !== null) {
                        commentCard.style.height = String((videoPlayer.offsetHeight + otherInfoCard.offsetHeight) * 0.9) + "px";
                    }
                })();
            };
        },

对于评论区的内容区域,其在首次进入时因v-if不成立而未加载,若直接使用document.getElementById则将获取到undefined,需要在nextTick中获取:

            handleClick(name) {
                if(name === "2") {
                    this.$nextTick(() => {
                        let commentCard = document.getElementById("commentCard");
                        if(commentCard !== null) {
                            let videoPlayer = document.getElementById("videoPlayer");
                            let otherInfoCard = document.getElementById("otherInfoCard");
                            commentCard.style.height = String((videoPlayer.offsetHeight + otherInfoCard.offsetHeight) * 0.9) + "px";
                        }
                    })
                }
                this.menuName = String(name)
            },

 

4.实际调用后台接口

首次进入时初始化,分页页数与页码改变触发事件,评论提交触发事件时均需调用分页查询接口,提交时需调用保存接口:


            async getCommentData() {
                let res = await get('/v1/courseComment/findPage',{
                    courseId: this.$route.query.id,
                    pageNum: this.commentPageNum,
                    pageSize: this.commentPageSize
                });
                this.commentData = res.data
            },
            pageChange(p) {
                this.commentPageNum = p;
                document.documentElement.scrollTop = 0;
                document.getElementById("commentCard").scrollTop = 0;
                this.getCommentData();
            },
            pageSizeChange(p) {
                this.commentPageSize = p;
                document.documentElement.scrollTop = 0;
                document.getElementById("commentCard").scrollTop = 0;
                this.getCommentData();
            },
            async commentSubmit() {
                if(this.commentModel === '') {
                    this.$Message.info("评论不能为空!");
                }
                else {
                    let res = await post('/v1/courseComment/save',{
                        courseId: this.$route.query.id,
                        commentName: this.$store.state.user.userName,
                        commentContext: this.commentModel,
                        commentIcon: this.$store.state.user.avatarImgPath
                    });
                    if(res.code === 200000) {
                        this.$Message.success("评论发送成功!");
                        this.commentPageNum = 1;
                        document.documentElement.scrollTop = 0;
                        document.getElementById("commentCard").scrollTop = 0;
                        this.commentModel = '';
                        this.getCommentData();
                    }
                    else {
                        this.$Message.error(res.msg);
                    }
                }
            }

 

5.实现效果

页面效果如下:

可以使用Java中的SimpleDateFormat类将String类型的时间转换为Date对象,然后再对Date对象进行处理。 首先,我们先将时间段转换为【2020-11-20 00:00:00,2021-10-09 23:59:59】,即将结束时间改为当天的最后一秒: ```java String startTime = "2020-11-20 09:09:09"; String endTime = "2021-10-09 10:10:10"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date startDate = sdf.parse(startTime); Date endDate = sdf.parse(endTime); Calendar endCalendar = Calendar.getInstance(); endCalendar.setTime(endDate); endCalendar.set(Calendar.HOUR_OF_DAY, 23); endCalendar.set(Calendar.MINUTE, 59); endCalendar.set(Calendar.SECOND, 59); endDate = endCalendar.getTime(); String newEndTime = sdf.format(endDate); System.out.println(newEndTime); ``` 输出结果为:2021-10-09 23:59:59 接下来,我们可以使用一个循环,每次增加一天,输出该天的开始时间和结束时间: ```java Calendar calendar = Calendar.getInstance(); calendar.setTime(startDate); while (calendar.getTime().before(endDate)) { Date startOfDay = calendar.getTime(); calendar.add(Calendar.DAY_OF_MONTH, 1); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); Date endOfDay = calendar.getTime(); System.out.println(sdf.format(startOfDay) + " - " + sdf.format(endOfDay)); } // 输出最后一天的开始时间和结束时间 Date startOfDay = calendar.getTime(); System.out.println(sdf.format(startOfDay) + " - " + newEndTime); ``` 输出结果为: ``` 2020-11-20 09:09:09 - 2020-11-21 00:00:00 2020-11-21 00:00:00 - 2020-11-22 00:00:00 2020-11-22 00:00:00 - 2020-11-23 00:00:00 ... 2021-10-07 00:00:00 - 2021-10-08 00:00:00 2021-10-08 00:00:00 - 2021-10-09 00:00:00 2021-10-09 00:00:00 - 2021-10-09 23:59:59 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值