vue js 对象下的原型

by Neo Ighodaro

由新Ighodaro

如何使用Vue.js和Pusher创建实时原型反馈应用程序 (How to create a realtime prototype feedback app using Vue.js and Pusher)

Here is a screen recording of what our application will be able to do once we are done building it:

这是完成应用程序构建后的应用程序屏幕截图:

我们将需要构建我们的应用程序的要求 (Requirements we will need to build our application)

Before we get started, we need to have a few things in place first. Some of the requirements are as follows:

在开始之前,我们需要首先做好一些准备。 一些要求如下:

  • Knowledge of PHP & the Laravel framework.

    了解PHP和Laravel框架。

  • Knowledge of JavaScript (ES6).JavaScript知识(ES6)。
  • Knowledge of Vue.js.了解Vue.js。
  • PHP 7.0+ installed locally on your machine.PHP 7.0+在您的计算机上本地安装。
  • Laravel CLI installed locally.

    Laravel CLI在本地安装。

  • Composer installed locally.

    Composer在本地安装。

  • NPM and Node.js installed locally.

    NPM和Node.js本地安装。

  • A Pusher application. Create one on pusher.com.

    Pusher应用程序。 在pusher.com上创建一个。

Once you have verified that you have the above requirements, we can start creating our application.

确认您具有上述要求后,我们就可以开始创建应用程序了。

设置我们的原型反馈应用程序 (Setting up our prototype feedback application)

Let us get started with setting up our application. Create a new Laravel application using the command below:

让我们开始设置应用程序。 使用以下命令创建一个新的Laravel应用程序:

$ laravel new your_application_name

When the installation is complete, cd to the application directory. Open the .env file and let us make a couple of changes in the file.

安装完成后, cd到应用程序目录。 打开.env文件,让我们在文件中进行一些更改。

设置我们的数据库和迁移 (Setting up our Database and Migrations)

The first thing to do is set up our database and create its migrations. Let’s start by setting up the database. Replace the configuration items below:

首先要做的是建立数据库并创建其迁移。 让我们从设置数据库开始。 替换以下配置项:

DB_CONNECTION=mysqlDB_HOST=127.0.0.1DB_PORT=3306DB_DATABASE=homesteadDB_USERNAME=homesteadDB_PASSWORD=secret

with:

与:

DB_CONNECTION=sqlite

This will now make the application use SQLite as its database choice. In your terminal, run the command below to create a new SQLite database:

现在,这将使应用程序使用SQLite作为其数据库选择。 在您的终端中,运行以下命令以创建新SQLite数据库:

$ touch database/database.sqlite

Now let’s create some migrations which will create the required tables to the database. In your terminal, run the following command to create the migrations we will need:

现在,让我们创建一些迁移,这些迁移将创建数据库所需的表。 在您的终端中,运行以下命令来创建我们将需要的迁移:

$ php artisan make:model Photo --migration --controller$ php artisan make:model PhotoComment --migration

The above command will create a model and then the --migration and --controller flags will instruct it to create a migration and a controller alongside the model.

上面的命令将创建一个模型,然后--migration--controller标志将指示它创建迁移,并在模型旁边创建一个控制器。

For now, we are interested in the Model and the migration. Open the two migration files created in the ./database/migrations directory. First, we’ll edit the CreatePhotosTable class. Replace the content of the up method with the following below:

目前,我们对模型和迁移感兴趣。 打开在./database/migrations目录中创建的两个迁移文件。 首先,我们将编辑CreatePhotosTable类。 用以下内容替换up方法的内容:

public function up(){    Schema::create('photos', function (Blueprint $table) {        $table->increments('id');        $table->string('url')->unique();        $table->string('image')->unique();        $table->timestamps();    });}

This will create the photos table when the migrations are run using the artisan command. It will also create new columns inside the table, as specified above.

使用artisan命令运行迁移时,这将创建photos表。 如上所述,它还将在表内创建新列。

Open the second migration class, CreatePhotoCommentsTable, and replace the up method with the contents below:

打开第二个迁移类CreatePhotoCommentsTable ,然后用以下内容替换up方法:

public function up(){    Schema::create('photo_comments', function (Blueprint $table) {        $table->increments('id');        $table->unsignedInteger('photo_id');        $table->text('comment');        $table->integer('top')->default(0);        $table->integer('left')->default(0);        $table->timestamps();        $table->foreign('photo_id')->references('id')->on('photos');    });}

This will create the table photo_comments when the migration is run, and will also create a foreign key to the photos table.

运行迁移时,这将创建表photo_comments ,还将创建photos表的外键。

Now go to your terminal and run the command below to run the migrations:

现在转到终端并运行以下命令来运行迁移:

$ php artisan migrate

This should now create the database tables.

现在,这应该创建数据库表。

设置模型 (Setting up the models)

Now that we have run our migrations, we need to make some changes to our model file so that it can work better with the table.

现在,我们已经运行了迁移,我们需要对模型文件进行一些更改,以使其可以更好地与表一起使用。

Open the Photo model and replace the contents with the following:

打开“ Photo模型并将内容替换为以下内容:

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Photo extends Model{    protected $with = ['comments'];    protected $fillable = ['url', 'image'];    public function comments()    {        return $this->hasMany(PhotoComment::class);    }}

In the above, we have added the fillable property which stops us from having mass assignment exceptions when trying to update those columns using Photo::create. We also set the with property which just eager loads the comments relationship.

在上面的代码中,我们添加了fillable属性,该属性使我们在尝试使用Photo::create更新这些列时避免出现批量分配异常。 我们还设置了with属性,它只是渴望加载comments关系。

We have defined an Eloquent relationship comments that just says the Photo has many PhotoComments.

我们已经定义了一个雄辩的关系comments ,该comments仅表示该Photo具有许多PhotoComments

Open the PhotoComment model and replace the contents with the following:

打开PhotoComment模型,并将内容替换为以下内容:

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class PhotoComment extends Model{    protected $fillable = ['photo_id', 'comment', 'top', 'left'];    protected $appends = ['position'];    public function getPositionAttribute()    {        return [            'top' => $this->attributes['top'],             'left' => $this->attributes['left']        ];    }}

Just like the Photo model, we have defined the fillable property. We also use Eloquent accessors to configure a new property called position which is then appended because we specified that in the appends property.

就像Photo模型一样,我们定义了fillable属性。 我们还使用雄辩的访问器来配置一个称为position的新属性,然后将其追加,因为我们在appends属性中指定了该属性。

为我们的应用程序设置前端 (Setting up the frontend for our application)

The next thing we want to do is set up the front end of our application. Let’s start by installing a few NPM packages that we will need in the application. In your Terminal app, run the command below to install the necessary packages:

接下来要做的是设置应用程序的前端。 让我们从安装应用程序中需要的一些NPM软件包开始。 在终端应用程序中,运行以下命令以安装必要的软件包:

$ npm install --save laravel-echo pusher-js vue2-dropzone@^2.0.0$ npm install

This will install Laravel Echo, the Pusher JS SDK, and vue-dropzone. We will need these packages to handle realtime events later.

这将安装Laravel Echo , Pusher JS SDK和vue-dropzone 。 我们将需要这些软件包来稍后处理实时事件。

When the packages have been installed successfully, we can now start adding some HTML and JavaScript.

成功安装软件包后,我们现在可以开始添加一些HTML和JavaScript。

Open the ./routes/web.php file and let’s add some routes. Replace the contents of the file with the contents below:

打开./routes/web.php文件,然后添加一些路由。 用以下内容替换文件的内容:

<?phpRoute::post('/feedback/{image_url}/comment', 'PhotoController@comment');Route::get('/feedback/{image_url}', 'PhotoController@show');Route::post('/upload', 'PhotoController@upload');Route::view('/', 'welcome');

In the code above, we have defined a few routes. The first one will be handling POSTed feedback. The second route will display the image that is to receive feedback. The third route will handle uploads, and the final route will display the homepage.

在上面的代码中,我们定义了一些路由。 第一个将处理POST ed反馈。 第二条路线将显示要接收反馈的图像。 第三条路线将处理上传,最后一条路线将显示主页。

Now open the ./resources/views/welcome.blade.php file and in there replace the contents with the following HTML code:

现在打开./resources/views/welcome.blade.php文件,并在其中用以下HTML代码替换内容:

<!doctype html><html lang="{{ app()->getLocale() }}"><head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1">    <meta name="csrf-token" content="{{csrf_token()}}">    <title>Upload to get Feedback</title>    <link href="https://fonts.googleapis.com/css?family=Roboto:400,600" rel="stylesheet" type="text/css">    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">    <link rel="stylesheet" href="{{ asset('css/app.css') }}"></head><body>    <div id="app">        <div class="flex-center position-ref full-height">            <div class="content">                <uploadarea></uploadarea>            </div>        </div>    </div>    <script src="js/app.js"></script></body></html>

This is a simple HTML document, and if you look closely, you will see a reference to an uploadarea tag which does not exist in HTML but is instead a Vue component.

这是一个简单HTML文档,如果仔细观察,您会看到对uploadarea标记的引用,该标记在HTML中不存在,而是Vue组件。

Open the ./resources/assets/sass/app.scss file and paste the following code below the import statements:

打开./resources/assets/sass/app.scss文件,并将以下代码粘贴到import语句下面:

html, body {    background-color: #fff;    color: #636b6f;    font-family: 'Roboto', sans-serif;    font-weight: 100;    height: 100vh;    margin: 0;}.full-height {    height: 100vh;}.flex-center {    align-items: center;    display: flex;    justify-content: center;}.position-ref {    position: relative;}.content {    text-align: center;}.m-b-md {    margin-bottom: 30px;}.dropzone.dz-clickable {    width: 100vw;    height: 100vh;    .dz-message {        span {            font-size: 19px;            font-weight: 600;        }    }}#canvas {    width: 90%;    margin: 0 auto;    img {        width: 100%;    }}.modal {    text-align: center;    padding: 0!important;    z-index: 9999;  }.modal-backdrop.in {    opacity: 0.8;    filter: alpha(opacity=80);}.modal:before {    content: '';    display: inline-block;    height: 100%;    vertical-align: middle;    margin-right: -4px;}.modal-dialog {    display: inline-block;    text-align: left;    vertical-align: middle;}.image-hotspot {    position: relative;    > img {        display: block;        height: auto;        transition: all .5s;    }}.hotspot-point {    z-index: 2;    position: absolute;    display: block;    span {        position: relative;        display: flex;        justify-content: center;        align-items: center;        width: 1.8em;        height: 1.8em;        background: #cf00f1;        border-radius: 50%;        animation: pulse 3s ease infinite;        transition: background .3s;        box-shadow: 0 2px 10px rgba(#000, .2);        &:after {            content: attr(data-price);            position: absolute;            bottom: 130%;            left: 50%;            color: white;            text-shadow: 0 1px black;            font-weight: 600;            font-size: 1.2em;            opacity: 0;            transform: translate(-50%, 10%) scale(.5);            transition: all .25s;        }    }    svg {        opacity: 0;        color: #cf00f1;        font-size: 1.4em;        transition: opacity .2s;    }    &:before,    &:after  {        content: '';        position: absolute;        top: 0;        left: 0;        width: 100%;        height: 100%;        border-radius: 50%;        pointer-events: none;    }    &:before {        z-index: -1;        border: .15em solid rgba(#fff, .9);        opacity: 0;        transform: scale(2);        transition: transform .25s, opacity .2s;    }    &:after {        z-index: -2;        background:#fff;        animation: wave 3s linear infinite;    }    &:hover{        span {            animation: none;            background: #fff;            &:after {                opacity: 1;                transform: translate(-50%, 0) scale(1);            }        }        svg {            opacity: 1;        }        &:before {            opacity: 1;            transform: scale(1.5);            animation: borderColor 2s linear infinite;        }        &:after {            animation: none;            opacity: 0;        }    }}@-webkit-keyframes pulse{    0%, 100% { transform: scale(1); }    50% { transform: scale(1.1); }}@keyframes pulse{    0%, 100% { transform: scale(1); }    50% { transform: scale(1.1); }}.popover {    min-width: 250px;}

Save the file and exit. Now let’s move on to creating our Vue components.

保存文件并退出。 现在让我们继续创建Vue组件。

使用Vue创建我们的原型反馈应用程序的功能 (Using Vue to create the functionalities of our prototype feedback app)

Open the ./resources/assets/js/app.js file and in there we’ll create our Vue component. In this file find the line below:

打开./resources/assets/js/app.js文件,然后在其中创建Vue组件。 在此文件中找到以下行:

Vue.component('example', require('./components/Example.vue'));

and replace it with:

并替换为:

Vue.component('uploadarea', require('./components/UploadArea.vue'));Vue.component('feedback', require('./components/FeedbackCanvas.vue'));

Now let’s create our first Vue component. In the ./resources/assets/js/components directory create a file called UploadArea.vue. In the new file paste in the following:

现在,让我们创建第一个Vue组件。 在./resources/assets/js/components目录中,创建一个名为UploadArea.vue的文件。 在新文件中粘贴以下内容:

<template>    <dropzone ref="dropzone" id="dropzone"            url="/upload"            accepted-file-types="image/*"            v-on:vdropzone-success="showImagePage"            :headers="csrfHeader"            class="flex-center position-ref full-height">        <input type="hidden" name="csrf-token" :value="csrfToken">    </dropzone></template><script>import Dropzone from 'vue2-dropzone';const LARAVEL_TOKEN = document.head.querySelector('meta[name="csrf-token"]').contentexport default {    components: { Dropzone },    data() {        return {            csrfToken: LARAVEL_TOKEN,            csrfHeader: { 'X-CSRF-TOKEN': LARAVEL_TOKEN }        }    },    methods: {        showImagePage: (file, response) => {            if (response.url) {                return window.location = `/feedback/${response.url}`;            }        }    },    mounted () {        this.$refs.dropzone.dropzone.on('addedfile', function (file) {            if (this.files.length > 1) {                this.removeFile(this.files[0])            }        })    }}</script>

In the template section, we are simply using the Vue dropzone package to define an area through which files can be uploaded. You can view the documentation here.

template部分,我们仅使用Vue dropzone包定义一个可以上传文件的区域。 您可以在此处查看文档。

In the script section, we get the Laravel CSRF token from the header of the page and import the Dropzone component into our current Vue component.

script部分,我们从页面的标题中获取Laravel CSRF令牌,并将Dropzone组件导入到我们当前的Vue组件中。

In the methods property, we define a showImagePage method that just redirects the user to the image page after the image has been successfully uploaded. In the mounted method, we limit the dropzone file to allow one file upload at a time.

methods属性中,我们定义了showImagePage方法,该方法仅在成功上传图像后将用户重定向到图像页面。 在mounted方法中,我们限制dropzone文件,以允许一次上传一个文件。

Let us create our next Vue component. In the ./resources/assets/js/components directory, create a new file called FeedbackCanvas.vue and paste in the following:

让我们创建下一个Vue组件。 在./resources/assets/js/components目录中,创建一个名为FeedbackCanvas.vue的新文件,并粘贴以下内容:

<template>    <div class="feedback-area">        <div class="content">            <div id="canvas">                <div class="image-hotspot" id="imghotspot">                    <transition-group name="hotspots">                        <a                        href="#"                        class="hotspot-point"                        v-for="(comment, index) in image.comments"                        v-bind:style="{ left: comment.position.left+'%', top: comment.position.top+'%' }"                        :key="index"                        @click.prevent                        data-placement="top"                        data-toggle="popover"                        :data-content="comment.comment"                        >                            <span>                                <svg class="icon icon-close" viewBox="0 0 24 24">                                    <path d="M18.984 12.984h-6v6h-1.969v-6h-6v-1.969h6v-6h1.969v6h6v1.969z"></path>                                </svg>                            </span>                        </a>                    </transition-group>                    <img ref="img" :src="'/storage/'+image.image" id="loaded-img"  @click="addCommentPoint">                </div>            </div>        </div>        <add-comment-modal :image="image"></add-comment-modal>    </div></template>

We have defined the template for our Vue component. This is the area where the image will be displayed and where feedback will be given.

我们已经为Vue组件定义了template 。 这是显示图像并提供反馈的区域。

Let’s break some parts of it down a little. The a tag has a bunch of attributes set to it. The v-for loops through each comment/feedback the image has.

让我们将其中的一些部分分解一下。 在a标签有一堆设置为它的属性。 v-for循环遍历图像的每个注释/反馈。

The v-bind:style applies a style attribute to the a tag using the left and top properties of the comment/feedback. We also have the :data-content, data-toggle and data-placement which Bootstrap needs for its Popovers.

v-bind:style使用注释/反馈的lefttop属性将style属性应用于a标签。 Bootstrap的Popovers还需要:data-contentdata-toggledata-placement

The img tag has the @click event that fires the function addCommentPoint when an area of the image is clicked. And finally, there’s a Vue component add-comment-modal that accepts a property image. This component will display a form so anyone can leave a comment.

img标签具有@click事件,当单击图像的某个区域时,该事件会触发功能addCommentPoint 。 最后,还有一个Vue组件add-comment-modal ,它接受属性image 。 该组件将显示一个表单,因此任何人都可以发表评论。

In this same file, after the closing template tag, paste in the following code:

在该文件的结束template标记之后,粘贴以下代码:

<script>    let AddCommentModal = require('./AddCommentModal.vue')    export default {        props: ['photo'],        components: { AddCommentModal },        data() {            return { image: this.photo }        },        mounted() {            let vm = this            Echo.channel(`feedback-${this.photo.id}`)                .listen('.added', (e) => {                    // Look through the comments and if no comment matches the                     // existing comments, add it                    if (vm.image.comments.filter((comment) => comment.id === e.comment.id).length === 0) {                        vm.image.comments.push(e.comment)                        $(document).ready(() => $('[data-toggle="popover"]').popover())                    }                })        },        created() {            /** Activate popovers */            $(document).ready(() => $('[data-toggle="popover"]').popover());            /** Calculates the coordinates of the click point */            this.calculateClickCordinates = function (evt) {                let rect = evt.target.getBoundingClientRect()                return {                    left: Math.floor((evt.clientX - rect.left - 7) * 100 / this.$refs.img.width),                    top: Math.floor((evt.clientY - rect.top - 7) * 100 / this.$refs.img.height)                }            }            /** Removes comments that have not been saved */            this.removeUnsavedComments = function () {                var i = this.image.comments.length                while (i--) {                    if ( ! this.image.comments[i]['id']) {                        this.image.comments.splice(i, 1)                    }                }            }        },        methods: {            addCommentPoint: function(evt) {                let vm       = this                let position = vm.calculateClickCordinates(evt)                let count    = this.image.comments.push({ position })                // Show the modal and add a callback for when the modal is closed                let modalElem = $("#add-modal")                modalElem.data({"comment-index": count-1, "comment-position": position})                modalElem.modal("show").on("hide.bs.modal", () => vm.removeUnsavedComments())            }        },    }</script>

? The**created** and**mounted** methods are hooks that are called automatically during the creation of the Vue component. You can learn about the Vue lifecycle here.

他* *created**一个ND * *mounted** 编制方法是被创建的Vue组件期间自动调用挂钩。 您可以 在这里获得有关Vue生命周期的信息。

In the mounted method, we use Laravel Echo to listen to a Pusher channel. The channel name depends on the ID of the image currently being viewed. Each image will have broadcasts on a different channel based on the ID of the image.

mounted方法中,我们使用Laravel Echo收听Pusher频道。 频道名称取决于当前正在观看的图像的ID。 每个图像将基于图像的ID在不同的频道上进行广播。

When the added event is triggered on the feedback-$id channel, it looks through the available image.comments and, if the comment broadcasted does not exist, it adds it to the comments array.

当在feedback-$id通道上触发added事件时,它将通过可用的image.comments查找,如果广播的注释不存在,则将其添加到注释数组。

In the create method, we activate Bootstrap popovers, define a function that calculates the coordinates of the click point, and we define a function that removes comments that have not been saved from the image.comments array.

create方法中,我们激活Bootstrap弹出窗口,定义一个函数来计算单击点的坐标,并定义一个函数来删除尚未从image.comments数组中保存的image.comments

Under methods we define the addCommentPoint method which calculates the click coordinates, and then launches a new Bootstrap modal which is going to be created in the add-comment-modal Vue component.

methods我们定义addCommentPoint方法,该方法计算点击坐标,然后启动一个新的Bootstrap模态,该模态将在add-comment-modal Vue组件中创建。

For Laravel Echo to work, we need to open the ./resources/assets/js/bootstrap.js file and add the code below at the bottom of the file:

为了使Laravel Echo正常工作,我们需要打开./resources/assets/js/bootstrap.js文件,并在文件底部添加以下代码:

import Echo from 'laravel-echo'window.Pusher = require('pusher-js');window.Echo = new Echo({    broadcaster: 'pusher',    key: 'PUSHER_KEY',    encrypted: true,    cluster: 'PUSHER_CLUSTER'});

You should replace PUSHER_KEY and PUSHER_CLUSTER with the key and cluster for your Pusher application.

您应该用Pusher应用程序的密钥和群集替换PUSHER_KEYPUSHER_CLUSTER

Now lets create our next Vue component, AddCommentModal.vue. It is already referenced in our FeedbackCanvas.vue Vue component.

现在,让我们创建下一个Vue组件AddCommentModal.vue 。 我们的FeedbackCanvas.vue Vue组件已经引用了它。

Create an AddCommentModal.vue file in the same directory as our other Vue components. In this file, paste in the code below:

在与其他Vue组件相同的目录中创建一个AddCommentModal.vue文件。 在此文件中,粘贴以下代码:

<template>    <div id="add-modal" class="modal fade" role="dialog" data-backdrop="static" data-keyboard="false">        <div class="modal-dialog">            <div class="modal-content">                <form method="post" :action="'/feedback/'+photo.url+'post'" @submit.prevent="submitFeedback()">                    <div class="modal-header">                        <h4 class="modal-title">Add Feedback</h4>                    </div>                    <div class="modal-body">                        <textarea name="feedback" id="feedback-provided" cols="10" rows="5" class="form-control" v-model="feedback" placeholder="Enter feedback..." required minlength="2" maxlength="2000"></textarea>                    </div>                    <div class="modal-footer">                        <button type="submit" class="btn btn-primary pull-right">Submit</button>                        <button type="button" class="btn btn-default pull-left" data-dismiss="modal">Cancel</button>                    </div>                </form>            </div>    </div>    </div></template><script>export default {    props: ['image'],    data() {        return { photo: this.image, feedback: null }    },    methods: {        submitFeedback: function () {            let vm = this            let modal = $('#add-modal')            let position = modal.data("comment-position")            // Create url and payload            let url = `/feedback/${this.photo.url}/comment`;            let payload = {comment: this.feedback, left: position.left, top: position.top}            axios.post(url, payload).then(response => {                this.feedback = null                modal.modal('hide')                vm.photo.comments[modal.data('comment-index')] = response.data                $(document).ready(() => $('[data-toggle="popover"]').popover())            })        }    }}</script>

In the template section, we have defined a typical Bootstrap modal. In the modal form, we have attached a call to submitFeedback() which is triggered when the form is submitted.

template部分,我们定义了典型的Bootstrap模式。 在模式表单中,我们已附加了对submitFeedback()的调用,该调用在表单提交时触发。

In the script section, we have defined the submitFeedback() method in the methods property of the Vue component. This function simply sends a comment to the backend for storage. If there is a favorable response from the API, the Bootstrap modal is hidden and the comment is appended to the image.comments array. The Bootstrap popover is then reloaded so it picks up the changes.

script部分中,我们在Vue组件的methods属性中定义了submitFeedback()方法。 此功能只是将注释发送到后端进行存储。 如果API响应良好,则会隐藏Bootstrap模式,并将注释附加到image.comments数组。 然后会重新加载Bootstrap弹出窗口,以便拾取更改。

With that final change, we have defined all our Vue components. Open your terminal and run the command below to build your JS and CSS assets:

经过最后的更改,我们定义了所有Vue组件。 打开终端并运行以下命令来构建JS和CSS资产:

$ npm run dev

Great! Now let’s build the backend.

大! 现在让我们构建后端。

为我们的原型反馈应用程序创建端点 (Creating the Endpoints for our prototype feedback application)

In your terminal, enter the command below:

在您的终端中,输入以下命令:

$ php artisan make:event FeedbackAdded

This will create an event class called FeedbackAdded. We will use this file to trigger events to Pusher when we add some feedback.This will make feedback appear in realtime to anyone looking at the image.

这将创建一个名为FeedbackAdded的事件类。 添加一些反馈后,我们将使用此文件触发事件给Pusher,这将使反馈实时显示给任何查看图像的人。

Open the PhotoController class and replace the contents with the code below:

打开PhotoController类,然后将内容替换为以下代码:

<?phpnamespace App\Http\Controllers;use App\Events\FeedbackAdded;use App\{Photo, PhotoComment};class PhotoController extends Controller{    public function show($url)    {        $photo = Photo::whereUrl($url)->firstOrFail();        return view('image', compact('photo'));    }    public function comment(string $url)    {        $photo = Photo::whereUrl($url)->firstOrFail();        $data = request()->validate([            "comment" => "required|between:2,2000",            "left" => "required|numeric|between:0,100",            "top"  => "required|numeric|between:0,100",        ]);        $comment = $photo->comments()->save(new PhotoComment($data));        event(new FeedbackAdded($photo->id, $comment->toArray()));        return response()->json($comment);    }    public function upload()    {        request()->validate(['file' => 'required|image']);        $gibberish = md5(str_random().time());        $imgName = "{$gibberish}.".request('file')->getClientOriginalExtension();        request('file')->move(public_path('storage'), $imgName);        $photo = Photo::create(['image' => $imgName, 'url' => $gibberish]);        return response()->json($photo->toArray());    }}

In the above, we have a show method which shows an image so people can leave feedback on it. Next, there is the comment method that saves a new comment on an image. The final method is the upload method that simply uploads an image to the server and saves it to the database.

在上面,我们有一个show方法来显示图像,以便人们可以在其上留下反馈。 接下来,有一个comment方法,可以在图像上保存新的注释。 最后一种方法是上upload方法,该方法只是将图像upload服务器并将其保存到数据库。

Let us create the view for the show method. Create a new file in the ./resources/views directory called image.blade.php. In this file, paste the code below:

让我们为show方法创建视图。 在./resources/views目录中创建一个名为image.blade.php的新文件。 在此文件中,粘贴以下代码:

<!doctype html><html lang="{{ app()->getLocale() }}"><head>    <meta charset="utf-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1">    <meta name="csrf-token" content="{{csrf_token()}}">    <title>Laravel</title>    <link href="https://fonts.googleapis.com/css?family=Roboto:400,600" rel="stylesheet" type="text/css">    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">    <link rel="stylesheet" href="{{ asset('css/app.css') }}"></head><body>    <div id="app">        <feedback :photo='@json($photo)'></feedback>    </div>    <script src="{{asset('js/app.js')}}"></script></body></html>

In the above, the only thing that stands out is the feedback tag. It is basically in reference to the feedback Vue component we built earlier in the article. Every other thing is just basic Blade and HTML.

在上面,唯一引人注目的是feedback标签。 它基本上是参考我们在本文前面构建的反馈Vue组件。 其他所有东西只是基本的Blade和HTML。

Now that we have created the view, we need to add the directory for uploads defined in the upload method. In your terminal, run the command below:

现在,我们已经创建了视图,我们需要添加上upload方法中定义的upload目录。 在您的终端中,运行以下命令:

$ php artisan storage:link

This command will create a symlink from the ./storage directory to the ./public/storage directory. If you look in the ./public directory you should see the symlink.

此命令将创建从./storage目录到./public/storage目录的符号链接。 如果查看./public目录,则应该看到符号链接。

Now that we have created the backend to support our web application, we need to add Pusher to the backend so that the comments made are broadcasted and can be picked up by other people browsing the image.

现在,我们已经创建了支持Web应用程序的后端,我们需要将Pusher添加到后端,以便广播所发表的评论,并且其他浏览该图像的人也可以将其提取。

使用Pusher将实时功能添加到原型反馈应用程序中 (Adding realtime functionality to the prototype feedback app using Pusher)

Open your terminal and enter the command below to install the Pusher PHP SDK:

打开终端,然后在下面输入命令以安装Pusher PHP SDK :

$ composer require pusher/pusher-php-server "~3.0"

Open the .env file and scroll to the bottom and configure the Pusher keys as seen below:

打开.env文件并滚动到底部,然后按如下所示配置Pusher键:

PUSHER_APP_ID="PUSHER_ID"PUSHER_APP_KEY="PUSHER_KEY"PUSHER_APP_SECRET="PUSHER_SECRET"

Also in the same file, look for the BROADCAST_DRIVER and change it from log to pusher.

同样在同一文件中,查找BROADCAST_DRIVER并将其从log更改为pusher

Next, open the ./config/broadcasting.php and scroll to the pusher key. Replace the options key of that configuration with the code below:

接下来,打开./config/broadcasting.php并滚动到pusher键。 用以下代码替换该配置的options键:

// ...'options' => [    'cluster' => 'PUSHER_CLUSTER',    'encrypted' => true], // ...

? Remember to replace thePUSHER_ID,PUSHER_KEY,PUSHER_SECRET andPUSHER_CLUSTER with the values from your Pusher application.

[R 请记得替换求解P USHER_ID, P USHER_KEY, P USHER_SECRET一个NDP USHER_CLUSTER 从推应用瓦特i个值。

Now, open the FeedbackAdded class and replace the contents with the code below:

现在,打开FeedbackAdded类,并将内容替换为以下代码:

<?phpnamespace App\Events;use Illuminate\Broadcasting\Channel;use Illuminate\Queue\SerializesModels;use Illuminate\Foundation\Events\Dispatchable;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;class FeedbackAdded implements ShouldBroadcast{    use Dispatchable, InteractsWithSockets, SerializesModels;    public $comment;    public $photo_id;    public function __construct(int $photo_id, array $comment)    {        $this->comment = $comment;        $this->photo_id = $photo_id;    }    public function broadcastOn()    {        return new Channel("feedback-{$this->photo_id}");    }    public function broadcastAs()    {        return 'added';    }}

In the class above, we define the comment object and the photo_id which will be used to compose the channel name in the broadcastOn method. We also define the broadcastAs method, which will allow us to customize the name of the event being sent to Pusher.

在上面的类中,我们定义了comment对象和photo_id ,它们将用于在broadcastOn方法中组成频道名称。 我们还定义了broadcastAs方法,这将使我们能够自定义发送给Pusher的事件的名称。

That’s all. Now, let’s run our application. In your terminal, run the code below:

就这样。 现在,让我们运行我们的应用程序。 在您的终端中,运行以下代码:

$ php artisan serve

This should start a new PHP server and you can then use that to test your application. Go to the URL given and you should see your application.

这应该启动一个新PHP服务器,然后您可以使用它来测试您的应用程序。 转到给出的URL,您应该会看到您的应用程序。

结论 (Conclusion)

In this article, we have successfully created a prototype application’s feedback feature that will allow designers to share their designs with others and receive feedback on them.

在本文中,我们成功创建了原型应用程序的反馈功能,该功能将使设计人员可以与他人共享他们的设计并接收有关他们的反馈。

If you have questions or feedback, please leave them as a comment below. The source code to the application is available on GitHub.

如果您有任何疑问或反馈,请在下面留下它们作为评论。 该应用程序的源代码可在GitHub上找到 。

This post first appeared on the Pusher blog.

这篇文章首先出现在Pusher博客上 。

翻译自: https://www.freecodecamp.org/news/how-to-create-a-realtime-prototype-feedback-app-using-vuejs-and-pusher-daeddae0e055/

vue js 对象下的原型

vue js 对象下的原型_如何使用Vue.js和Pusher创建实时原型反馈应用程序相关推荐

  1. arkit与现实世界距离比_如何使用ARKit和Pusher构建实时增强现实测量应用程序

    arkit与现实世界距离比 by Esteban Herrera 由Esteban Herrera 如何使用ARKit和Pusher构建实时增强现实测量应用程序 (How to Build a Rea ...

  2. js 返回上一页面_构建大型 Vue.js 项目的10条建议

    下面是我在开发大型 Vue 项目时的最佳实践.这些技巧将帮助你开发更高效.更易于维护和共享的代码. 今年做自由职业的时候,我有机会开发了一些大型 Vue 应用程序.我所说的这些项目,Vuex stor ...

  3. js对象数组计算总计_如何计算数组中的对象

    js对象数组计算总计 Knowing how to quickly iterate through an array and count objects is deceptively simple. ...

  4. vue页面加载时闪现_如何解决Vue.js显示数据的时,页面闪现

    下面我就为大家分享一篇解决使用Vue.js显示数据的时,页面闪现原始代码的问题,具有很好的参考价值,希望对大家有所帮助. 今天开始学习Vue.js的使用,但是在学习过程中发现一个问题,那就是页面加载数 ...

  5. vue页面加载时闪现_解决使用Vue.js显示数据的时,页面闪现原始代码

    今天开始学习Vue.js的使用,但是在学习过程中发现一个问题,那就是页面加载数据时,原始代码会闪现一下.查访各方资料,终的解决方法. 第一步.加入一段css代码 [v-cloak] { display ...

  6. vue 数组删除 dome没更新_详解Vue响应式原理

    摘要: 搞懂Vue响应式原理! 作者:浪里行舟 原文:深入浅出Vue响应式原理 Fundebug经授权转载,版权归原作者所有. 前言 Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是 ...

  7. vue中集合取第一个_快速学习Vue框架(知识点集合)

    学习Vue的小伙伴速度看过来,快速学习Vue框架知识点集合贴来啦.建议收藏,尤其基础并不是很扎实的同学,本篇集合贴就是你日后工作的参考手册. 基础知识: ·vue的生命周期:beforeCreate/ ...

  8. vue遇到ie兼容问题如何处理_详解vue 兼容IE报错解决方案

    IE 页面空白 报错信息 此时页面一片空白 报错原因 Babel 默认只转换新的 JavaScript 语法(如箭头函数),而不转换新的 API ,比如 Iterator.Generator.Set. ...

  9. 静态原型_如何在静态,低保真和高保真原型之间做出选择

    静态原型 现代技术设计要求高度的交互性:导航栏随用户的滚动位置而变化,单击框展开以显示和隐藏支持信息,将列表拖放到重新排序等.设计人员必须考虑到所有可能的用户交互来在他们的产品中创建最简化,最直观的用 ...

最新文章

  1. 「THUPC2018」赛艇 / Citing
  2. python里有常量吗?(没有,但可自行定义)
  3. Java Swing/AWT API速查手册
  4. python sphinx_Python Sphinx使用实例及问题解决
  5. 玩耍redis遇到的问题之记录
  6. 枚举学习文摘 — 框架设计(第2版) CLR Via C#
  7. 时间操作(JavaScript版)—最简单比较两个时间格式数据的大小
  8. 【5年Android从零复盘系列之三十四】Android存储(9):腾讯MMKV 高性能键值存储组件详解
  9. 使用阿里云接口进行银行卡四要素实名认证
  10. 分布式理论与分布式架构设计理论
  11. CH32F103与STM32F103在USB接口上的差异与使用
  12. go revel框架搭建
  13. coldfusion_ColdFusion教程:第一部分
  14. Java实现Excel导入导出(附Demo)
  15. 区别:符号变量和常变量
  16. SRC挖掘信息收集之JS文件中的秘密
  17. 如何拿下头条号原创标?这里有份6000字的指南
  18. PAKDD 21: GRAPH INFOCLUST 明尼苏达
  19. phd计算机考试,美国计算机PHD院校申请难度有多大?
  20. 中国如何面对金融危机

热门文章

  1. sqlserver无法连接到服务器的常见原因和解决办法
  2. 主窗体的常用属性 c# 1615011872
  3. 类与对象的小结 java 1614782140
  4. 动态对象泛型数组绑定控件 0107
  5. qdir安装 多窗口资源管理软件
  6. python-批量插入多条数据-pymysql-executemany方法
  7. linux指令:输出重定向与追加- 输出重定向 - 表示追加
  8. explain for connection用法
  9. 《HTML5+CSS3网页设计入门必读》——第1章 标记简史1.1 从IETF到W3C:HTML 4的诞生过程...
  10. SQL Server 之 在与SQLServer建立连接时出现与网络相关的或特定于实例的错误