用Vue實做信用卡&商品操作
在開始前請先安裝sweetalert套件
npm install --save-dev sweetalert@2.1.2
bootstrap
在resources/sass
,在這支檔案引用bootstrap
@import "node_modules/bootstrap/scss/bootstrap";
config
在config
資料夾,建立card.php
信用卡資料.txt
Controller
在專案的目錄下,輸入指令新增CardController.php
php artisan make:controller CardController
card.php
在CardsController
使用Config
的card.php
,拿到信用卡資料
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Config;
class CardController extends Controller
{
public function getCardData()
{
$card = Config::get('card');
return [
'items' => $card['items'],
'cardItems' => $card['cardItems'],
];
}
}
JS
在resources/js
路徑底下,新增card.js
檔案,並且引用List.vue
、Message.vue
import cardList from "./components/card/List.vue"
import cardMessage from "./components/card/Message.vue"
let app = new Vue({
el: "#app",
components: {
"card-list": cardList,
"card-message": cardMessage
},
data: {
items: [],
cardItems: [],
showErrorMessage: false,
errorMessageText: "",
url: '/card'
},
mounted() {
axios.post(this.url).then((response) => {
this.items = response.data.items
this.cardItems = response.data.cardItems
}).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);
}
})
},
computed: {
cardData() {
const data = []
const cardItems = this.cardItems
const items = this.items
_.forEach(cardItems, (value, key) => {
let isUse = false
_.mapKeys(value, (card, cardkey) => {
_.forEach(items, (obj) => {
if (cardkey === obj.card && obj.status === "1") {
isUse = true
}
})
const obj = {
cardName: card.cardName,
cardNumber: card.full,
cardId: card.cardName + key,
cardValue: card.last,
isUseCard: isUse,
}
data.push(obj)
})
})
return data
},
},
methods: {
saveCardData(cardObj, index) {
const length = this.cardItems.length
const id = Math.random().toString(36).substr(6)
this.cardItems.push(id)
const itenObj = {}
itenObj[id] = cardObj
this.cardItems[length] = itenObj
this.items[index].card = id
},
deleteCardData(index) {
try {
this.cardData.splice(index, 1)
this.cardItems.splice(index, 1)
} catch (e) {
this.showErrorMessage = true
this.errorMessageText = "刪除失敗!"
}
},
},
})
畫面載入時會先執行mounted()
,用axios
從url
變數的Route
,從CardsController
取得信用卡資料,這時cardData()
會組信用卡列表的資料,saveCardData()
是儲存商品綁定哪張信用卡資料,deleteCardData()
則是移除信用卡資料。
component
新增四個component
檔案,AddData.vue
、List.vue
、Message.vue
、Select.vue
<!-- AddData.vue -->
<script>
export default {
data() {
return {
cardName: '',
card1: '',
card2: '',
card3: '',
card4: '',
}
},
computed: {
cardData() {
const obj = {}
obj.cardName = this.cardName
obj.full = `${this.card1}-${this.card2}-${this.card3}-${this.card4}`
obj.last = this.card4
obj.first = this.card1
return obj
},
},
methods: {
checkCardData() {
let message = ''
const data = this.cardData
if (data.cardName === '') {
message = '信用卡名稱不能為空'
} else if (data.full.length < 19) {
message = '卡號請輸入16碼數字'
} else if (/[A-Za-z\u4e00-\u9fa5]/.test(data.full)) {
message = '卡號不能輸入中英文'
}
if (message === '') {
const cardNum = _.split(data.full, '-')
_.forEach(cardNum, (value) => {
if (value.length !== 4) {
message = '每個卡號區間請輸入4碼數字'
return
}
})
}
this.$emit('send-card', message, data)
},
},
}
</script>
AddData.vue
- 新增信用卡資料,將信用卡資料傳到card.js
,並更新信用卡列表,cardData()
是重新組信用卡卡號資料,將資料傳到文字框裡,checkCardData()
檢查輸入的信用卡資料是否正確。
<!-- List.vue -->
<script>
import CardSelect from './Select.vue'
export default {
components: {
'card-select': CardSelect
},
props: {
item: {
type:Object
},
cardData: {
type:Array
},
productIndex: {
type:Number
},
},
data() {
return {
selectItem: this.item,
btnSuccess: 'btn btn-success',
btnDanger: 'btn btn-danger',
btnEdit: 'btn btn-primary',
successText: '啟用',
dangerText: '停止',
editText: '編輯',
isShow: false,
isStatus: this.item.status == '1',
messageText: '',
isDisabled: false,
}
},
computed: {
itemData() {
const select = this.selectItem
const item = _.find(this.cardData, select.card)
this.isDisabled = false
return item
},
cardName() {
const itemData = this.itemData
const item = this.item
const key = _.findKey(itemData, (e, key) => {
return key == item.card
})
let name = (key === undefined || key === null) ? '' : itemData[key].cardName
if (name === '') {
name = '未綁卡'
this.isDisabled = true
}
return name
},
cardSelected() {
const itemData = this.itemData
const item = this.item
const key = _.findKey(itemData, (e, key) => {
return key == item.card
})
const selected = (key === undefined || key === null) ? '' : itemData[key].last
return selected
},
},
methods: {
changeStatus(status) {
if (status === '0') {
this.item.status = '1'
this.isStatus = true
} else {
this.item.status = '0'
this.isStatus = false
}
},
changeCard() {
if (this.isShow) {
this.isShow = false
} else {
this.isShow = true
}
},
saveCard(isAdd, CardObj) {
this.isShow = false
if (!isAdd) {
let key = ''
_.mapKeys(CardObj, (card, cardkey) => {
key = cardkey
})
this.selectItem.card = key
} else {
this.$emit('save-new-card', CardObj, this.index)
}
},
},
}
</script>
List.vue
是顯示商品使用信用卡現況,這裡有引用到Select.vue
,是切換信用卡資料用的。
props
傳進來的item
是所有商品資料,cardData
與index
是將資料到Select.vue
的信用卡資料。computed
的itemData()
是將每個信用卡資料組成物件,cardName()
是itemData()
拿信用卡名稱,cardSelected()
則是信用卡卡號的末四碼。
methods
的changeStatus()
是切換目前商品的信用卡狀態,changeCard()
是商品要切換信用卡資料,saveCard()
是將信用卡資料,使用emit
傳遞,function
名稱為save-new-card
,將信用卡物件
與索引值
資料傳到card.js
的saveCardData()
。
<!-- Message.vue -->
<template>
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">
<h3>{{ message }}</h3>
</slot>
</div>
<div class="modal-footer">
<slot name="footer">
<div class="form-check form-check-inline">
<input type="button" class="btn btn-primary" id="save" name="save" value="確認" @click="$emit('close')">
</div>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
props:{
message:{
type:String
}
}
}
</script>>
Message.vue
是顯示提示訊息用的,將新增、更新、刪除信用卡資料的訊息傳到這裡。
<!-- Select.vue -->
<script>
import CardAddData from './AddData.vue'
import swal from 'sweetalert'
export default {
components:{
'card-add-data' : CardAddData
},
props:{
cardData:{
type:Array
},
cardSelected:{
type:String
},
isShow:{
type:Boolean
},
productIndex:{
type:Number
},
},
data:function(){
return {
'cardLastData' : '',
'selectedData' : '',
'sendId' : 'send' + this.productIndex,
'selected' : this.cardSelected,
'btnSuccess' : 'btn btn-success',
'btnDanger' : 'btn btn-danger',
'showModal': false,
'showCardList' : this.isShow,
'isError' : false,
'isRepeat' : false
}
},
computed: {
cardAllData(){
let cardArray = []
let cardData = this.cardData
_.forEach(cardData, function (value, key) {
_.mapKeys(value, function(card, cardkey){
cardArray.push(card)
})
})
return cardArray
},
cardIndexData(){
let cardIdArray = []
let cardName = 'cardname' + this.productIndex + '_'
let length = this.cardData.length
this.cardLastData = cardName + length
for (let i = 0; i < length; i++) {
let id = cardName + i
cardIdArray.push(id)
}
return cardIdArray
},
selectedData(){
let obj = 'add'
let selected = this.selected
_.forEach(this.cardData, function (value, key) {
_.mapKeys(value, function(card, cardkey){
let cardLast = value[cardkey].last
if(selected == cardLast){
obj = value
return
}
})
})
return obj
}
},
watch:{
isShow(newVal, oldVal){
if(newVal == true){
this.selected = this.cardSelected
}
this.showCardList = newVal
}
},
methods: {
changeCard(){
if(this.selectedData == 'add'){
this.showModal = true
}else{
this.$emit('save-card', false, this.selectedData)
}
},
sendCard(message, CardObj){
let isError = false
if(message != ''){
isError = true
this.messageText = message
}else{
let cardData = this.cardData
_.forEach(cardData, function (value, key) {
_.mapKeys(value, function(card, cardkey){
if(CardObj.full == card.full){
isError = true
return
}
})
})
if(!isError){
this.messageText = '新增成功!'
this.showModal = false
this.$emit('save-card', true, CardObj)
}else{
this.messageText = '卡號有重複!'
}
}
if(this.messageText != ''){
swal({
title: this.messageText,
confirmButtonColor: '#e6b930',
icon: !isError ? 'success':'error',
showCloseButton: true
})
}
this.isError = isError
},
closeMessage(){
if(!this.isError){
this.showCardList = false
}
}
},
}
</script>
Select.vue
是顯示選擇信用卡列表,這裡有引用到AddData.vue
是在新增信用卡資料用的,sweetalert
是美化訊息視窗的套件。
props
傳進來的cardData
是目前商品資料,cardSelected
是目前選到的信用卡資料,isShow
是決定顯示全部信用卡資料視窗,productIndex
是目前商品的索引值。
computed
的cardAllData()
是重新組好信用卡的物件,cardIndexData()
是重新組信用卡資料的索引值,避免與其他商品重複,selectedData()是重新組選擇信用卡的資料。
watch
的isShow()
是顯示全部信用卡資料視窗時,將選擇到信用卡初始化。
methods
的changeCard()
是商品切換或新增信用卡,對應顯示信用卡視窗或傳值,sendCard()
是儲存信用卡資料,會先檢查到信用卡資料後,會顯示提示訊息是否有成功。
商品頁面
<!-- card.blade.php -->
<html>
<head>
<meta name="csrf-token" content="{{ csrf_token() }}">
</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 id="app" class="justify-content-center align-items-center">
<div class="row" style="margin-bottom: 60px;">
<div class="col">
<div class="card">
<div class="card-body">
<h5 class="card-title">Card List</h5>
<div class="form-inline" v-for="(card, index) in cardData" :key="index"
style="height: 50px;">
<div class="form-group">
<label>@{{ card.cardName }}:@{{ card.cardNumber }}</label>
<div class="col">
<small v-if="card.isUseCard" class="text-danger">卡片正使用中!</small>
<input type="button" v-else class="btn btn-primary" id="delete" name="delete"
value="刪除" v-on:click="deleteCardData(index)">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<card-list v-for="(item, index) in items" :item="item" :index="index" :key="item.id"
:card-data="cardItems" @save-new-card="saveCardData"></card-list>
<card-message v-if="showErrorMessage" @close="showErrorMessage = false" :message="errorMessageText"></card-message>
</div>
</div>
<script src="{{mix('js/app.js')}}"></script>
<script src="{{mix('js/card.js')}}"></script>
<link rel="stylesheet" type="text/css" href="{{mix('/css/app.css')}}">
</body>
</html>
Routes
在routes/web.php
新增網址,指定要顯示card.blade.php
Route::get('/card', function () {
return view('card');
});
Route::post('/card', 'CardController@getCardData');
上面步驟完成之後,我們要進行編譯,請在webpack.mix.js
檔案,新增card.js
、app.sass
檔案
mix.js('resources/js/app.js', 'public/js')
.js('resources/js/card.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/card
在頁面上方顯示信用卡是否正在使用中
,沒有正在使的可將信用卡資料做刪除
,有在使用的顯示卡片正使用中!
。
每個商品都會綁定一張信用卡,若沒有會顯示信用卡名稱:未綁卡
,啟用按鈕
會註解起來,需要編輯
信用卡資料才可以啟用
,新增
或更換
信用卡時,系統會偵測卡號是否與其他信用卡卡號相同
,卡號資料要16碼數字
,不能輸入中英文
,每個卡號區間
要輸入4碼數字
,當系統有偵測到輸入的資料有問題時,會提示使用者。