27. Vue 与 API 串接练习

课程练习

此阶段是实际串接 API 的差异,串接 API 後的一页式产品新增、删除、修改的页面。
先制作登入页验证使用者帐号,再导入产品页。

登入页面

// 登入页面 html
<div id="app">
    <div class="container">
      <form class="form-sognin" @submit.prevent="signin">
        <h1 class="h3  mb-3 font-weight-normal">请先登入</h1>
        <div class="form-group">
          <label for="inputEmail" class="sr-only">Email</label>
          <input type="email" id="inputEmail" 
            v-model="user.email" class="form-control" 
            placeholder="请输入Email" required
            autofocus>
        </div>
        <div class="form-group">
          <label for="inputPassword" class="sr-only">Email</label>
          <input type="password" id="inputPassword" 
            v-model="user.password" class="form-control" 
            placeholder="请输入密码"
            required>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">
          登入
        </button>
      </form>
    </div>
  </div>
// JS 登入验证
new Vue({

      //指定
      el: '#app',
      
      //资料
      data: {
        user: {
          email: '',
          password: '',
        },
      },

      //方法
      methods: {
        //登入 , 登入AJAX後 , 把 token(代码) 和 expired(到期时间) 存到浏览器 cookie , 在跳转页面
        signin() {
          const api = `https://course-ec-api.hexschool.io/api/auth/login`; //连接登入api
          axios.post(api, this.user).then((response) => {
            const token = response.data.token;  //建立token
            const expired = response.data.expired; //建立expires
            //存到浏览器 cookie , 并把 expires 设置有效时间
            document.cookie = `token=${token};expires=${new Date(expired * 1000)};path=/
            `;
            window.location = 'Products.html'; //执行以上程序码後 , 跳转到产品页
            //* 一定要包在里面 , 放到外面一层会因为非同步原因 , 会先执行跳转页面
          }).catch((error) => { //填写错误 , 报出错误讯息
            console.log(error);
          });
        },
      },

    })  

产品页面

// 登入页面 html
<div id="app" class="container mt-3">
        <div class="text-right mt-4">
            <button class="btn btn-primary" @click="openModal('new')">
                建立新的产品
            </button>
        </div>
        <table class="table mt-4">
            <thead>
                <tr>
                    <th width="15%">分类</th>
                    <th>产品名称</th>
                    <th>产品图像</th>
                    <th width="10%">原价</th>
                    <th width="10%">售价</th>
                    <th width="10%">是否启用</th>
                    <th width="15%">编辑</th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="(item , index) in products" :key="index">
                    <td>{{ item.category }}</td>
                    <td>{{ item.title }}</td>
                    <td><template>
                            <img class="thumb-img" :src="item.imageUrl" alt="">
                        </template></td>
                    <td class="text-right">{{ item.origin_price }}</td>
                    <td class="text-right">{{ item.price }}</td>
                    <td>
                        <span v-if="item.enabled" class="text-success">启用</span>
                        <span v-else>未启用</span>
                    </td>
                    <td>
                        <div class="btn-group">
                            <button class="btn btn-outline-primary btn-sm mr-2"
                                @click="openModal('edit' , item)" 
                                :disabled="loadingBtn === item.id">
                                <span class="spinner-border spinner-border-sm" 
                                role="status" aria-hidden="true" 
                                v-if="loadingBtn === item.id"></span>
                                编辑</button>
                            <button class="btn btn-outline-danger btn-sm"
                                @click="openModal('delete' , item)">删除</button>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>
        <!--分页-->
        <pagination :pages="pagination" @update="getProducts"></pagination>


        <!-- productModal -->
        <div id="productModal" class="modal fade" tabindex="-1" role="dialog" 
        aria-labelledby="exampleModalLabel"
            aria-hidden="true">
            <modal :api="api" :temp-product="tempProduct" @updata="getProducts"></modal>
        </div>
        <!--productModal END -->

        <div id="delProductModal" class="modal fade" tabindex="-1" role="dialog" 
        aria-labelledby="exampleModalLabel"
            aria-hidden="true">
            <div class="modal-dialog" role="document">
                <div class="modal-content border-0">
                    <div class="modal-header bg-danger text-white">
                        <h5 id="exampleModalLabel" class="modal-title">
                            <span>删除产品</span>
                        </h5>
                        <!-- 关闭按钮-->
                        <button type="button" class="close" data-dismiss="modal" 
                        aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <!--modal-header END -->

                    <div class="modal-body">
                        是否删除 <strong class="text-danger">{{ tempProduct.title }}</strong> 
                    商品(删除後将无法恢复)。
                    </div>
                    <!--modal-bady END -->

                    <div class="modal-footer">
                        <button class="btn btn-outline-secondary" type="button" 
                        data-dismiss="modal">取消</button>
                        <button class="btn btn-danger" type="button" @click="delProduct">
                        确认删除</button>
                    </div>
                    <!--modal-footer END -->

                </div>
            </div>
        </div>
        <!--delProductModal END -->

    </div>
// 产品页JS
new Vue({

    //指定    
    el: '#app',

    //资料
    data: {
        products: [],
        pagination: {},
        tempProduct: {
            imageUrl: []
        },
        api: {
            uuid: '',
            path: ''
        },
        token: '',
        isNew: '',
        loadingBtn: '',
    },


    //创建
    created() {
        //取得存在 cookie 中的 token
        this.token = document.cookie.replace(/(?:(?:^|.*;\s*)token\s*\=\s*([^;]*).*$)|^.*$/, "$1");
        //预设带入 token
        axios.defaults.headers.common.Authorization = `Bearer ${this.token}`;
        this.getProducts();
    },

    //方法
    methods: {
        //开启视窗
        openModal(isNew, item) {
            switch (isNew) {
                //新增
                case 'new':
                    this.tempProduct = { imageUrl: [] };
                    $('#productModal').modal('show');
                    break;
                //编辑
                case 'edit':
                    //读取效果
                    this.loadingBtn = item.id;
                    //取得远端单笔资料
                    const url = `${this.api.path}${this.api.uuid}/admin/ec/product/${item.id}`;
                    //取得远端资料
                    axios.get(url).then((res) => {
                        this.tempProduct = res.data.data;
                        $('#productModal').modal('show');
                        this.loadingBtn = '';
                    }); 
                    break;
                //删除
                case 'delete':
                    this.tempProduct = Object.assign({}, item);
                    $('#delProductModal').modal('show');
                    break;
                default:
                    break;
            }
        },

        //删除资料
        delProduct() {
            if (this.tempProduct.id) {
                const id = this.tempProduct.id;
                this.products.forEach((item, i) => {
                    if (item.id === id) {
                        this.products.splice(i, 1);
                        this.tempProduct = {};
                    }
                });
            }
            $('#delProductModal').modal('hide');
        },

        //取得资料
        getProducts(num = 1) {
            //取得远端後台管理API
            const url = `${this.api.path}${this.api.uuid}/admin/ec/products?page=${num}`;
            //取得远端资料
            axios.get(url).then((res) => {
                this.products = res.data.data;
                this.pagination = res.data.meta.pagination;

                if (this.tempProduct.id) {
                    this.tempProduct = {
                        imageUrl: [],
                    };  
                    $('#productModal').modal('hide');                  
                };

            });
        },
    },
});

<<:  从零开始-30日练习开发iOS APP-IQKeyboardManagerSwift Day-29

>>:  [Day27] VSCode Plugin - WakaTime

Day27 - 区块链社会学读後感(下) 价值、治理

继上篇主要以区块链三个特徵叙写,下篇笔者将探讨区块链的价值、治理,这篇会是比较实用一点的生活应用面向...

Day13:今天来聊一下Microsoft Defender for Endpoint的配置警报和检测

Microsoft Defender for Endpoint 提供警报和检测的配置选项。 配置包括...

系统分析师养成之路—商业思维(外师分享)补充

关於系统分析师、专案经理、商业分析师⋯这些IT角色来说,「商业思维」真的太重要!所以除了跟大家分享我...

DES-EDE3-CBC

初始化向量(IV)是一个随机数,通常是一次使用的数字,即一个随机数。它用於删除密文中的重复模式,以增...

业务连续性委员会(Business Continuity Committee)

-董事委员会 董事会认为必要时可设立任何委员会。有些委员会通常是法律或法规所要求的,例如审计委员会...