初始化项目

composer create-project laravel/laravel comments

创建数据库,配置.env 文件。我这里的数据库是comments

使用系统自带的验证程序。

php artisan make:auth
php artisan migrate

使用npm命令安装依赖和tailwindcss

cnpm install tailwindcss --save-dev
cnpm install

本教程涉及到的代码我都将上传至:

https://github.com/leienshu/comments

构建组件结构

先使用下面的命令来观察sass文件或者js文件的一些变动情况。

npm run watch

清除home.blade.php文件中@section 和 @endsection 部分的内容。

home.blade.php

对了,上面的Laravel标题在env文件中可以改成Comments Demo。

好了,现在我们进入 resources/js/components 目录,新建一个新的Vue文件,名字叫:CommentsManager.vue ,具体内容如下:

<template>
    <div>
        <h1>Hello World</h1>
    </div>
</template>

<script>
    export default {
        data: function() {
            return {

          }
        },
    }
</script>

在 app.js 文件中注册上面新建的 vue 文件。把之前的example-component注释掉。

Vue.component('comments-manager', require('./components/CommentsManager.vue').default);

在 home.blade.php 文件的section部分添加下面的模板。

<comments-manager></comments-manager>
成功引入组件

用户界面有点丑,不过我们继续干吧。

显示评论

在CommentsManager的data属性里,我们增加一些评论数据。如下:

return {
                comments: [
                {
                    id: 1,
                    body: "咋整的呢?",
                    edited: false,
                    created_at: new Date().toLocaleString(),
                    author: {
                        id: 1,
                        name: '雷经纬',
                    }
                }
            ]
          }

现在我们就有了一些数据可以使用,我们可以开始在页面上显示它。但是,现在虽然可以将所有逻辑保存在CommentsManager组件中,但我们最终必须编写一些奇怪的方法来解析每个注释的详细信息。所以,我们最好为每个评论制作另一个组件。

在 comments-manager.vue 相同目录下,再新建一个 CommentItem.vue 文件。

<template>
    <div>
    
    </div>
</template>

<script>
    export default {
        data: function() {
            return {
                
          }
        },
    }
</script>

现在,让我们通过一个评论属性,包含所有评论的数据,然后将其添加到模板中。

<template>
    <div>
        <div>
            <p>{{comment.body}}</p>
        </div>
        <div>
            <p>{{comment.author.name}} <span>--</span>{{ comment.created_at}}</p>
        </div>
    </div>
</template>

<script>
    export default {
        props: {
                comment: {
                    required: true,
                    type: Object,
                }
            },
        data: function() {
            return {

          }
        },
    }
</script>

现在进入 CommentsManager.vue 文件来注册CommentItem。

<template>
    <div>
        <div>
            <comment v-for="comment in comments"
                     :key="comment.id"
                     :comment="comment">
            </comment>
        </div>
    </div>
</template>

<script>
    import comment from './CommentItem'
    export default {
        components: {
                    comment
                    },
        data: function() {
            return {
                comments: [
                {
                    id: 1,
                    body: "咋整的呢?",
                    edited: false,
                    created_at: new Date().toLocaleString(),
                    author: {
                        id: 1,
                        name: '雷经纬',
                    }
                }]
          }
        },
    }
</script>

上面的代码看懂了么?我们在<script>标签里面引入CommentItem的comment属性。在评论显示的地方用v-for和:key来提取评论。

显示评论

评论显示好了,但是还不能编辑,下面我们就来编辑评论。

编辑评论

因为我们的编辑功能并不是对所有人开放的,所以我们需要传递当前用户到CommentItem里。

为此,我们需要为我们的两个组件添加一个用户属性,然后对经过身份验证的用户添加传递。我们可以这样添加那个属性:

//CommentsManager
<comment v-for="comment in comments"
         :key="comment.id"
         :user="user"
         :comment="comment">
</comment>
...
props: {
    user: {
        required: true,
        type: Object,
    }
},

//CommentItem
props: {
    user: {
        required: true,
        type: Object,
    },
    comment: {
        required: true,
        type: Object,
    }
},

//home.blade.php
<comments-manager :user="{{ auth()->user() }}"></comments-manager>

在CommentItem中添加一个computed属性来判断当前用户是否可以编辑评论。

computed: {
    editable() {
        return this.user.id === this.comment.author.id;
    }
}

我们再给评论添加一个Edit按钮。

<div>
    <p>{{comment.body}}</p>
    <button v-if="editable">Edit</button>
</div>

上面的v-if可以确保只有当前用户才能点击这个Edit按钮。然后,我们再在CommentsManeger里面的comments里面添加一个对象。

comments: [
                    {
                        id: 1,
                        body: "咋整的呢?",
                        edited: false,
                        created_at: new Date().toLocaleString(),
                        author: {
                            id: 1,
                            name: '雷经纬',
                        }
                    },
                    {
                        id: 2,
                        body: "这样整的啊?",
                        edited: false,
                        created_at: new Date().toLocaleString(),
                        author: {
                            id: 2,
                            name: '王者归来',
                        }
                    },
                ]

出来的效果如下图:

当前用户显示Edit按钮

怎么样,现在的Edit还不能编辑,我们继续。

我们首先在CommentItem的组件数据中添加一个state属性,这样我们就可以跟踪编辑的时间。我们还要添加一个updatedComment属性,用来跟踪我们做的更改。

data: function() {
            return {
                state: 'default',
                data:{
                    body:this.comment.body,
                }

          }
        },

因为这里的state比较简单,我就用了字符串类型,其实也可以用布尔类型。

使用comment.body中的值初始化data.body,这样就可以让我们用正确的初始数据填充编辑表单。

绑定一个点击事件,将state设置为editing。

<button v-if="editable" @click="state = 'editing'">Edit</button>

接下来,让我们添加我们的编辑表单。

<template>
    <div>
        <div v-show="state === 'default'">
            <div>
                <p>{{comment.body}}</p>
                <button v-if="editable" @click="state = 'editing'">Edit</button>
            </div>
            <div>
                <p>{{comment.author.name}} <span>--</span>{{ comment.created_at}}</p>
            </div>
        </div>
        <div v-show="state === 'editing'">
            <div>
                <h3>Update Comment</h3>
            </div>
            <textarea v-model="data.body"
                      placeholder="Update comment"
                      class="border">
            </textarea>
            <div>
                <button>Update</button>
                <button>Cancel</button>
            </div>
        </div>
    </div>
</template>

上面用两个div来对应state的变化,我们再添加一个resetEdit的方法,用来重置data.body和state。

//Template
<button @click="resetEdit">Cancel</button>
...

//Vue Instance
methods: {
    resetEdit() {
        this.state = 'default';
        this.data.body = this.comment.body;
    }
}

继续添加一个saveEdit()的方法。

在这里,我们有几个选项可以将我们的更改返回到评论所在的父组件。我们可以直接使用 $parent.comments 去直接访问,也可以改变我们的 comment 属性。

但这些并不是最佳选择,因为它们和我们的组件结合过于紧密。不过,这样用也不是不行。不过,我建议我们还是使用event去搞。

在saveEdit()方法中,我们把state的设置回给它的默认值,并且我们这时候将修改评论这个事件传递过去。这样在CommentsManager我们将有一个updateComment方法,当我们的事件触发时就会调用它。

//CommentItem

//Template
<button @click="saveEdit">Update</button>

//Script
saveEdit() {
    this.state = 'default';

    this.$emit('comment-updated', {
        'id': this.comment.id,
        'body': this.data.body,
    });
}

//CommentsManager

//Template
<comment v-for="comment in comments"
         :key="comment.id"
         :user="user"
         :comment="comment"
         @comment-updated="updateComment($event)">
</comment>

//Script
methods: {
    updateComment($event) {
        let index = this.comments.findIndex((element) => {
            return element.id === $event.id;
        });

        this.comments[index].body = $event.body;
    }
}

再次打开浏览器,你就可以编辑评论了。

删除评论

跟上面的更新评论一样,我们增加一个按钮和事件就行了,具体代码如下:

//CommentItem

//Template
<div>
    <button @click="saveEdit">Update</button>
    <button @click="resetEdit">Cancel</button>
    <button @click="deleteComment">Delete</button>
</div>

//Script
deleteComment() {
    this.$emit('comment-deleted', {
        'id': this.comment.id,
    });
}

//Comments Manager

//Template
<comment v-for="comment in comments"
         :key="comment.id"
         :user="user"
         :comment="comment"
         @comment-updated="updateComment($event)"
         @comment-deleted="deleteComment($event)">
</comment>

//Script
deleteComment($event) {
    let index = this.comments.findIndex((element) => {
        return element.id === $event.id;
    });

    this.comments.splice(index, 1);
}

添加评论

搞完了更新删除评论,我们继续来搞添加评论吧。

我们搞得简单点,直接在CommentsManager里面增加。

<template>
    <div>
        <div>
            <div>
                <h2>Comments</h2>
            </div>
            <textarea placeholder="Add a comment"
                          class="border">
              </textarea>
            <div>
                <button>Save</button>
                <button>Cancel</button>
            </div>
        </div>
        <div>
            <comment v-for="comment in comments"
                     :key="comment.id"
                     :user="user"
                     :comment="comment"
                     @comment-updated="updateComment($event)"
                     @comment-deleted="deleteComment($event)">
            </comment>
        </div>
    </div>
</template>

为了实现添加功能,我们还需要添加一个saveComment的方法,并且添加一个data.body的属性到data,而且还要给textarea 添加一个v-model。

//template
<div>
            <div>
                <h2>Comments</h2>
            </div>
            <textarea v-model="comments.body"
                placeholder="Add a comment"
                class="border">
              </textarea>
            <div>
                <button @click="saveComment">Save</button>
                <button>Cancel</button>
            </div>
        </div>
//script
saveComment() {
                let newComment = {
                    id: this.comments[this.comments.length - 1].id + 1,
                    body: this.comments.body,
                    edited: false,
                    created_at: new Date().toLocaleString(),
                    author: {
                        id: this.user.id,
                        name: this.user.name,
                    }
                }

                this.comments.push(newComment);

                this.comments.body = '';
            },

打开浏览器,你会发现添加功能有了。

添加评论

总结

好了,今天先到这里,界面有点丑陋,不过几本的CURD已经实现了,我们下一篇文章继续实现增加Tailwind CSS,让界面优雅点。

使用Vue.js、Laravel和Tailwind CSS构建评论系统-(一)
Tagged on:         

One thought on “使用Vue.js、Laravel和Tailwind CSS构建评论系统-(一)

Leave a Reply

Your email address will not be published. Required fields are marked *