[Day 29] Laravel+MongoDB+Vue CRUD - part 2

Day28已建立好环境了,接下来开始制作CURD功能罗~/images/emoticon/emoticon08.gif

Controller

php artisan make:controller KeywordController

KeywordController负责进行实际对MongoDB的CRUD操作,再将资料回传到View用Vue作呈现

<?php

namespace App\Http\Controllers;

use App\Models\Keyword;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Pagination\Paginator;
use Carbon\Carbon;

class KeywordController extends Controller
{
    public function getKeywordView(){
        return view('keyword');
    }

    public function index(Request $request){
        $perPage = 5;
        $page = (int)$request->page;
        $from= ($page * $perPage) - ($perPage - 1);
        $keyword = Keyword::all()->splice($from, $perPage);
        $total = Keyword::count();
        $response = [
            'pagination' => [
                'total' => $total,
                'per_page' => $perPage,
                'current_page' => $page,
                'last_page' => ceil($total / $perPage),
                'from' => $from,
                'to' => $page * $perPage
            ],
            'keyword' => $keyword, 'add' => route('addKeyword'), 'update' => route('updateKeyword'),
            'delete' => route('deleteKeyword')
        ];

        return response()->json($response);
    }

    public function addKeywordData(Request $request)
    {
        $english_name = $request->english_name;
        $chinese_name = $request->chinese_name;
        $datetime = Carbon::now()->toDateTimeString();
        $status = 'success';
        $message = '新增成功!';

        try {
            Keyword::insert([
                'english_name' => $english_name, 
                'chinese_name' => $chinese_name,
                'created_at' => $datetime,
                'updated_at' => $datetime
            ]);
        } catch (Exception $e) {
            $status = 'error';
            $message = '新增失败!';
        }
        
        return [ 
            'status' => $status,
            'message' => $message 
        ];
    }

    public function updateKeywordData(Request $request){
        $english_name = $request->english_name;
        $chinese_name = $request->chinese_name;
        $datetime = Carbon::now()->toDateTimeString();
        $status = 'success';
        $message = '更新成功!';

        $data = [
            'english_name' => $english_name,
            'chinese_name' => $chinese_name,
            'updated_at' => $datetime
        ];

        try {
            Keyword::where('_id', $request->id)->update($data);
        } catch (Exception $e) {
            $status = 'error';
            $message = ' 更新失败!';
        }
        
        return [
            'status' => $status,
            'message' => $message 
        ];
    }

    public function deleteKeywordData(Request $request){
        $datetime = Carbon::now()->toDateTimeString();
        $status = 'success';
        $message = '删除成功!';

        try {
            Keyword::where('_id', $request->id)->delete();
        } catch (Exception $e) {
            $status = 'error';
            $message = '删除失败!';
        }
        
        return [
            'status' => $status,
            'message' => $message
        ];
    }

}

JS code

keyword.js里引用Edit.vueList.vue、NavbarPagination.vue

import keywordList from './components/keyword/List.vue';
import keywordEdit from './components/keyword/Edit.vue';
import NavbarPagination from './components/NavbarPagination.vue'

let app = new Vue({
    el: '#app',
    components: {
        'keyword-list': keywordList,
        'keyword-edit': keywordEdit,
        'navbar-pagination': NavbarPagination
    },
    data: {
        editTitle: '',
        showEdit: false,
        isAdd: false,
        keyword: [],
        urlAdd: '',
        urlUpdate: '',
        urlDelete: '',
        pagination: {},
        getPage:1,
        params: {
            'id': '',
            'enName': '',
            'chName': ''
        },
        keyWordDataIndex: null
    },
    mounted() {
        this.getKeywordData(1)
    },
    methods: {
        getPagination(getPage) {
            this.getKeywordData(getPage)
        },
        getKeywordData(page){
            axios.get('/getKeywordData?page=' + page).then(response => {
                this.keyword = response.data.keyword,
                this.urlAdd = response.data.add,
                this.urlUpdate = response.data.update,
                this.urlDelete = response.data.delete,
                this.pagination = response.data.pagination
            }).catch((error) => {
                //显示请求资料失败的错误讯息
                if (error.response){
                    //在log显示response错误的资料、状态、表头
                    console.log(error.response.data);
                    console.log(error.response.status);
                    console.log(error.response.headers);
                }else{
                    //在log显示r错误讯息
                    console.log('Error',error.message);
                }
                
            })
        },
        getAddKeyword() {
            this.editTitle = '新增关键字'
            this.showEdit = true
            this.isAdd = true
            this.params = {
                'id': '',
                'enName': '',
                'chName': ''
            }
        },
        updateKeyword(id, enName, chName, index) {
            this.editTitle = '更新关键字'
            this.showEdit = true
            this.params = {
                'id': id,
                'enName': enName,
                'chName': chName
            }
            this.keyWordDataIndex = index
        },
        updateKeywordData(params) {
            if (this.keyWordDataIndex != null) {
                this.keyword[this.keyWordDataIndex].chinese_name = params.chinese_name
                this.keyword[this.keyWordDataIndex].english_name = params.english_name
            }
        },
        deleteKeywordData(index) {
            this.keyWordDataIndex = index
            if (this.keyWordDataIndex != null) {
                this.keyword.splice(this.keyWordDataIndex, 1)
            }
        },
        isShowMessage(isSuccess, message){
            let isAdd =  this.isAdd
            swal({
                title: message,
                confirmButtonColor: "#e6b930",
                icon: isSuccess ? 'success':'error',
                showCloseButton: true
            }).then(function() {
                if (isSuccess && isAdd){
                    location.reload()
                }
            })

            if (isSuccess) {
                this.showEdit = false
            }
        }
    },
    
})

mounted
getKeywordData() 取得该分页的关键字资料

method

  • getPagination() 切换分页时,会更新分页对应的关键字资料
  • getAddKeyword() 显示新增的视窗
  • updateKeyword() 显示更新的视窗
  • updateKeywordData() 更新目前的资料
  • deleteKeywordData() 移除目前的资料
  • isShowMessage() 显示提示讯息

Vue component

  • Edit.vue
  • List.vue
  • NavbarPagination.vue

Edit.vue

<!-- Edit.vue -->
<script>
import swal from "sweetalert"

export default {
	props: {
		title: {
			type:String
		},
		params: {
			type:Object
		},
		urlAdd: {
			type:String
		},
		urlUpdate: {
			type:String
		},
		isAdd: {
			type:Boolean
		}
	},
	data() {
		return {
			enName: this.params.enName,
			chName: this.params.chName,
			messageText: '',
		}
	},
	methods: {
		checkKeyword(enName, chName) {
			let isSuccess = false
			this.messageText = ''
			
			if (enName === '' && chName === '') {
				this.messageText = "英文或中文名称不能为空"
			} else if (enName === '') {
				this.messageText = "英文名称不能为空"
			} else if(/[\u4e00-\u9fa5]/.test(enName)) {
				this.messageText = "英文名称不能输入中文"
			} else if (chName === '') {
				this.messageText = "中文名称不能为空"
			} else if(/[A-Za-z]/.test(chName)) {
				this.messageText = "中文名称不能输入英文"
			} 

			if (this.messageText == '') {
				isSuccess = true
			} else {
				this.$emit('is-show-message', isSuccess, this.messageText)
			}
			return isSuccess
		},
		saveKeyword(){
			let enName = this.enName
			let chName = this.chName
			let url = this.isAdd ? this.urlAdd : this.urlUpdate
			let isSuccess = this.checkKeyword(enName, chName)

			if (isSuccess) {
				let data = {
					'english_name' : enName,
					'chinese_name' : chName,
				}

				if (!this.isAdd) {
					data['id'] = this.params.id
				}

				axios.post(url, data).then((response) => {
					let isSuccess = response.data.status == 'success' ? true : false
					if (isSuccess && !this.isAdd) {
						this.$emit('update-keyword-data', data)
					}
					this.$emit('is-show-message', isSuccess, response.data.message)
				}).catch((error) => {
					if (error.response) {
						console.log(error.response.data);
						console.log(error.response.status);
						console.log(error.response.headers);
					} else {
						console.log('Error', error.message);
					}
					this.$emit('is-show-message', false, '发生意外错误!')
				})
			}
			
		}
	},
}
</script>

Edit.vue是编辑关键字资料的视窗,sweetalert是能强大、美观又容易使用的dialog library。

props

  • title 传视窗的标题
  • params 关键字的资料
  • urlAdd 新增资料的Route
  • urlUpdate 新增资料的Route
  • isAdd 要新增资料的布林值

methods

  • checkKeyword()是检查关键字资料是否问题,有问题就使用emit传讯息文字
  • saveKeyword()是储存关键字资料用的

List.vue

<!-- List.vue -->
<script>
export default {
	props: {
		keyWordData: {
			type:Array
		},
		urlDelete: {
			type:String
		}
	},
	data() {
		return {
			
		}
	},
	methods: {
		deleteKeyword(id, index) {
			let url = this.urlDelete
			let params = {
                id: id
			}
			
			axios.post(url, params).then((response) => {
				let isSuccess = response.data.status == 'success' ? true : false
				if (isSuccess) {
					this.$emit('delete-keyword-data', index)
				}
                this.isShowMessage(isSuccess, response.data.message)
            }).catch((error) => {
                if (error.response) {
                    console.log(error.response.data);
                    console.log(error.response.status);
                    console.log(error.response.headers);
                } else {
                	console.log('Error', error.message);
                }
                this.$emit('is-show-message', false, '发生意外错误!')
            })
		}
	}
}
</script>

List.vue是显示关键字资料的表

props

  • keyWordData 传全部关键字资料
  • urlDelete 移除资料Route

methods
deleteKeyword() 移出关键字资料

NavbarPagination.vue

<!-- NavbarPagination.vue -->
<script>
export default {
    props:{
        paginationData:{
            type:Object
        }
    },
    data:function(){
        return {
            offset: 4
        }
    },
    computed: {
        isActived: function () {
            return this.paginationData.current_page;
        },
        pagesNumber: function () {
            if (!this.paginationData.to) {
                return [];
            }
            let from = this.paginationData.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            let to = from + (this.offset * 2);
            if (to >= this.paginationData.last_page) {
                to = this.paginationData.last_page;
            }
            let pagesArray = [];
            while (from <= to) {
                pagesArray.push(from);
                from++;
            }
            return pagesArray;
        }
    },
    methods: {
        changePage: function (page) {
            this.$emit('change-pagination', page)
        }
    }
}
</script>

NavbarPagination.vue是分页的导览列

props

  • paginationData 分页资料的物件

computed

  • isActived() 计算目前第几页
  • pagesNumber() 重新排列分页资料

methods

  • changePage() 切换分页时,将目前页数使用emit传递

查询页面

<!-- keyword.blade.php-->
<html>
    <head>
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <meta http-equiv="Access-Control-Allow-Origin" content="*" />
    </head>
    <style>
        .modal-mask {
            position: fixed;
            z-index: 9998;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
            display: table;
            transition: opacity 0.3s ease;
        }
    
        .modal-wrapper {
            display: table-cell;
            vertical-align: middle;
        }
    
        .modal-container {
            width: 300px;
            margin: 0px auto;
            padding: 20px 30px;
            background-color: #fff;
            border-radius: 2px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
            transition: all 0.3s ease;
            font-family: Helvetica, Arial, sans-serif;
        }
    
        .modal-header h3 {
            margin-top: 0;
            color: #42b983;
        }
    
        .modal-body {
            margin: 20px 0;
        }
    
        .modal-default-button {
            float: right;
        }
    
        /*
     * The following styles are auto-applied to elements with
     * transition="modal" when their visibility is toggled
     * by Vue.js.
     *
     * You can easily play with the modal transition by editing
     * these styles.
     */
    
        .modal-enter {
            opacity: 0;
        }
    
        .modal-leave-active {
            opacity: 0;
        }
    
        .modal-enter .modal-container,
        .modal-leave-active .modal-container {
            -webkit-transform: scale(1.1);
            transform: scale(1.1);
        }
    </style>
    <body>
        <div class="container">
            <div v-cloak id="app" class="content">
                <form action="" method="POST">
                    {{ csrf_field() }}
                    <h2 
                        id="title" 
                        class="text-center text-black font-weight-bold" 
                        style="margin-bottom:20px;">
                    关键字查询
                    </h2>
                    <div style="text-align:right">
                        <input type="button" id="btn_insert" class="btn btn-primary" @click="getAddKeyword()" value="新增" />
                    </div><br/>
                    <keyword-list
                        :key-word-data="keyword" 
                        :url-delete="urlDelete"
                        @update-keyword="updateKeyword"
                        @delete-keyword-data="deleteKeywordData"
                    >
                    </keyword-list>
                    <keyword-edit 
                        v-if="showEdit" 
                        @close="showEdit = false" 
                        :title="editTitle"
                        :is-add="isAdd"
                        :url-add="urlAdd"
                        :url-update="urlUpdate"
                        :params="params"
                        @update-keyword-data="updateKeywordData"
                        @is-show-message="isShowMessage"
                        
                    >
                    </keyword-edit>
                    <navbar-pagination 
                        @change-pagination="getPagination" 
                        :pagination-data="pagination"
                    >
                    </navbar-pagination>
                </form>
            </div>
        </div>
        <script src="{{mix('js/app.js')}}"></script>
        <script src="{{mix('js/keyword.js')}}"></script>
        <link rel="stylesheet" type="text/css" href="{{mix('/css/app.css')}}">
    </body>
</html>

设定Route

Route::get('/keyword', 'KeywordController@getKeywordView');

Route::get('/getKeywordData','KeywordController@index');

Route::get('/updateKeyword', 'KeywordController@updateKeywordData');

Route::post('/addKeyword', 'KeywordController@addKeywordData')
    ->name('addKeyword');

Route::post('/updateKeyword', 'KeywordController@updateKeywordData')
    ->name('updateKeyword');

Route::post('/deleteKeyword', 'KeywordController@deleteKeywordData')
    ->name('deleteKeyword');

编译档案

webpack.mix.js新增keyword.jsapp.sass档案进行边ㄧ

mix.js('resources/js/app.js', 'public/js')
   .js('resources/js/keyword.js', 'public/js')
   .sass('resources/sass/app.scss', 'public/css');
npm run watch

启动本机站台

$ php artisan serve
Starting Laravel development server: http://127.0.0.1:8000

启动之後,输入网址为http://127.0.0.1:8000/keyword

Resource
xampp-gmongodb
laravel-mongodb


<<:  Day28 - reversing.kr - Easy_ELF

>>:  [Day28] 第二十八课 Azure灾害复原(DRaaS)-1[进阶]

Re: 新手让网页 act 起来: Day25 - useMemo 和 useCallback

前言 在前面几天介绍了使用了 useCallback 或 useMemo 来做效能优化,不知道会不会...

Day28 Gin with SMTP Server

What is an SMTP Server? SMTP 全名为Simple Mail Transf...

[ 卡卡 DAY 28 ] - React Native 自制 radio button + textarea

今天天外飞来一笔说需要 RN 表单画面 emergency!!! 咦咦咦 啊怎麽只有 TextIn...

[Day28] Tableau 轻松学 - TabPy 备份与还原

前言 在勒索病毒盛行的年代,为资讯系统做好备份是最基本的工作,有效的备份除了可以抵挡病毒的攻击,同时...

Day07 建造APP(1)

昨天我们创造完Project後,我们今天要学的是怎麽打造App,你们可能会对这名子感到疑惑,想说这个...