課程部分模板 - hexschool/vue-course-api-wiki GitHub Wiki

Vue init 的設定檔參考

雖然課程中 ESLint 選擇為 YES,但在此推薦選擇 No。(對 ES6 及錯誤排除有一定掌握者可選擇 Yes)

? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? Yes // 可選擇 No
? Pick an ESLint preset Airbnb.     // 選擇 No,此項目則跳過
? Set up unit tests No
? Setup e2e tests with Nightwatch? No

產品的新增修改

<div class="modal fade" id="productModal" tabindex="-1" role="dialog"
  aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-lg" role="document">
    <div class="modal-content border-0">
      <div class="modal-header bg-dark text-white">
        <h5 class="modal-title" id="exampleModalLabel">
          <span>新增產品</span>
        </h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <div class="row">
          <div class="col-sm-4">
            <div class="form-group">
              <label for="image">輸入圖片網址</label>
              <input type="text" class="form-control" id="image"
                placeholder="請輸入圖片連結">
            </div>
            <div class="form-group">
              <label for="customFile">或 上傳圖片
                <i class="fas fa-spinner fa-spin"></i>
              </label>
              <input type="file" id="customFile" class="form-control"
                ref="files">
            </div>
            <img img="https://images.unsplash.com/photo-1483985988355-763728e1935b?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=828346ed697837ce808cae68d3ddc3cf&auto=format&fit=crop&w=1350&q=80"
              class="img-fluid" alt="">
          </div>
          <div class="col-sm-8">
            <div class="form-group">
              <label for="title">標題</label>
              <input type="text" class="form-control" id="title"
                placeholder="請輸入標題">
            </div>

            <div class="form-row">
              <div class="form-group col-md-6">
                <label for="category">分類</label>
                <input type="text" class="form-control" id="category"
                  placeholder="請輸入分類">
              </div>
              <div class="form-group col-md-6">
                <label for="price">單位</label>
                <input type="unit" class="form-control" id="unit"
                  placeholder="請輸入單位">
              </div>
            </div>

            <div class="form-row">
              <div class="form-group col-md-6">
              <label for="origin_price">原價</label>
                <input type="number" class="form-control" id="origin_price"
                  placeholder="請輸入原價">
              </div>
              <div class="form-group col-md-6">
                <label for="price">售價</label>
                <input type="number" class="form-control" id="price"
                  placeholder="請輸入售價">
              </div>
            </div>
            <hr>

            <div class="form-group">
              <label for="description">產品描述</label>
              <textarea type="text" class="form-control" id="description"
                placeholder="請輸入產品描述"></textarea>
            </div>
            <div class="form-group">
              <label for="content">說明內容</label>
              <textarea type="text" class="form-control" id="content"
                placeholder="請輸入產品說明內容"></textarea>
            </div>
            <div class="form-group">
              <div class="form-check">
                <input class="form-check-input" type="checkbox"
                  id="is_enabled">
                <label class="form-check-label" for="is_enabled">
                  是否啟用
                </label>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">取消</button>
        <button type="button" class="btn btn-primary">確認</button>
      </div>
    </div>
  </div>
</div>
<div class="modal fade" id="delProductModal" 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 class="modal-title" id="exampleModalLabel">
          <span>刪除產品</span>
        </h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        是否刪除 <strong class="text-danger">{{ tempProduct.title }}</strong> 商品(刪除後將無法恢復)。
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">取消</button>
        <button type="button" class="btn btn-danger"
          >確認刪除</button>
      </div>
    </div>
  </div>
</div>

製作錯誤的訊息回饋

<template>
  <div class="message-alert">
    <div class="alert alert-dismissible"
      :class="'alert-' + item.status"
      v-for="(item, i) in messages" :key="i">
      {{ item.message }}
      <button type="button" class="close" @click="removeMessage(i)" aria-label="Close">
        <span aria-hidden="true">&times;</span>
      </button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Navbar',
  data() {
    return {
      messages: [],
    };
  },
  methods: {
    updateMessage(message, status) {
      const timestamp = Math.floor(new Date() / 1000);
      this.messages.push({
        message,
        status,
        timestamp,
      });
      this.removeMessageWithTiming(timestamp);
    },
    removeMessage(num) {
      this.messages.splice(num, 1);
    },
    removeMessageWithTiming(timestamp) {
      const vm = this;
      setTimeout(() => {
        vm.messages.forEach((item, i) => {
          if (item.timestamp === timestamp) {
            vm.messages.splice(i, 1);
          }
        });
      }, 5000);
    },
  },
  created() {
    // const vm = this;

    // 自定義名稱 'messsage:push'
    // message: 傳入參數
    // status: 樣式,預設值為 warning
    // vm.$bus.$on('message:push', (message, status = 'warning') => {
    //   vm.updateMessage(message, status);
    // });
    // vm.$bus.$emit('message:push');
  },
};
</script>

<style scope>
.message-alert {
  position: fixed;
  max-width: 50%;
  top: 56px;
  right: 20px;
  z-index: 1100;
}
</style>

套用價格的 Filter 技巧

金額千分號 Filter

export default function (num) {
  const n = Number(num);
  return `$${n.toFixed(0).replace(/./g, (c, i, a) => {
    const currency = (i && c !== '.' && ((a.length - i) % 3 === 0) ? `, ${c}`.replace(/\s/g, '') : c);
    return currency;
  })}`;
}

取得產品列表

產品列表卡片

<div class="col-md-4 mb-4">
  <div class="card border-0 shadow-sm">
    <div style="height: 150px; background-size: cover; background-position: center"
      >
    </div>
    <div class="card-body">
      <span class="badge badge-secondary float-right ml-2">分類</span>
      <h5 class="card-title">
        <a href="#" class="text-dark">標題</a>
      </h5>
      <p class="card-text">內容</p>
      <div class="d-flex justify-content-between align-items-baseline">
        <!-- <div class="h5">2,800 元</div> -->
        <del class="h6">原價 2,800 元</del>
        <div class="h5">現在只要 1,400 元</div>
      </div>
    </div>
    <div class="card-footer d-flex">
      <button type="button" class="btn btn-outline-secondary btn-sm">
        <i class="fas fa-spinner fa-spin"></i>
        查看更多
      </button>
      <button type="button" class="btn btn-outline-danger btn-sm ml-auto">
        <i class="fas fa-spinner fa-spin"></i>
        加到購物車
      </button>
    </div>
  </div>
</div>

選購產品及加入購物車

<table class="table">
  <thead>
    <th></th>
    <th>品名</th>
    <th>數量</th>
    <th>單價</th>
  </thead>
  <tbody>
    <tr v-for="item in cart.carts">
      <td class="align-middle">
        <button type="button" class="btn btn-outline-danger btn-sm">
          <i class="far fa-trash-alt"></i>
        </button>
      </td>
      <td class="align-middle">
        {{ item.product.title }}
        <!-- <div class="text-success" v-if="item.coupon">
          已套用優惠券
        </div> -->
      </td>
      <td class="align-middle">{{ item.qty }}/{{ item.product.unit }}</td>
      <td class="align-middle text-right">{{ item.final_total }}</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <td colspan="3" class="text-right">總計</td>
      <td class="text-right">{{ cart.total }}</td>
    </tr>
    <tr>
      <td colspan="3" class="text-right text-success">折扣價</td>
      <td class="text-right text-success">{{ cart.final_total }}</td>
    </tr>
  </tfoot>
</table>
<div class="input-group mb-3 input-group-sm">
  <input type="text" class="form-control" placeholder="請輸入優惠碼">
  <div class="input-group-append">
    <button class="btn btn-outline-secondary" type="button">
      套用優惠碼
    </button>
  </div>
</div>

建立訂單及購物車頁面驗證

購物車表單

<div class="my-5 row justify-content-center">
  <form class="col-md-6">
    <div class="form-group">
      <label for="useremail">Email</label>
      <input type="email" class="form-control" name="email" id="useremail"
        v-model="form.user.email" placeholder="請輸入 Email" required>
      <span class="text-danger"></span>
    </div>
  
    <div class="form-group">
      <label for="username">收件人姓名</label>
      <input type="text" class="form-control" name="name" id="username"
        v-model="form.user.name" placeholder="輸入姓名">
      <span class="text-danger"></span>
    </div>
  
    <div class="form-group">
      <label for="usertel">收件人電話</label>
      <input type="tel" class="form-control" id="usertel" v-model="form.user.tel" placeholder="請輸入電話">
    </div>
  
    <div class="form-group">
      <label for="useraddress">收件人地址</label>
      <input type="text" class="form-control" name="address" id="useraddress" v-model="form.user.address"
        placeholder="請輸入地址">
      <span class="text-danger">地址欄位不得留空</span>
    </div>
  
    <div class="form-group">
      <label for="comment">留言</label>
      <textarea name="" id="comment" class="form-control" cols="30" rows="10" v-model="form.message"></textarea>
    </div>
    <div class="text-right">
      <button class="btn btn-danger">送出訂單</button>
    </div>
  </form>
</div>

結帳頁面

<div class="my-5 row justify-content-center">
  <form class="col-md-6" @submit.prevent="payOrder">
    <table class="table">
      <thead>
        <th>品名</th>
        <th>數量</th>
        <th>單價</th>
      </thead>
      <tbody>
        <tr v-for="item in order.products" :key="item.id">
          <td class="align-middle">{{ item.product.title }}</td>
          <td class="align-middle">{{ item.qty }}/{{ item.product.unit }}</td>
          <td class="align-middle text-right">{{ item.final_total }}</td>
        </tr>
      </tbody>
      <tfoot>
        <tr>
          <td colspan="2" class="text-right">總計</td>
          <td class="text-right">{{ order.total }}</td>
        </tr>
      </tfoot>
    </table>

    <table class="table">
      <tbody>
        <tr>
          <th width="100">Email</th>
          <td>{{ order.user.email }}</td>
        </tr>
        <tr>
          <th>姓名</th>
          <td>{{ order.user.name }}</td>
        </tr>
        <tr>
          <th>收件人電話</th>
          <td>{{ order.user.tel }}</td>
        </tr>
        <tr>
          <th>收件人地址</th>
          <td>{{ order.user.address }}</td>
        </tr>
        <tr>
          <th>付款狀態</th>
          <td>
            <span v-if="!order.is_paid">尚未付款</span>
            <span v-else class="text-success">付款完成</span>
          </td>
        </tr>
      </tbody>
    </table>
    <div class="text-right" v-if="order.is_paid === false">
      <button class="btn btn-danger">確認付款去</button>
    </div>
  </form>
</div>
⚠️ **GitHub.com Fallback** ⚠️