用vue作項目也有一段時間了,以前也是寫過關於vue和webpack構建項目的相關文章,你們有興趣能夠去看下webpack+vue項目實戰(一,搭建運行環境和相關配置)(這個系列一共有5篇文章,這是第一篇,其它幾篇文章連接就不貼了)。可是關於vue入門基礎的文章,我尚未寫過,那麼今天就寫vue入門的三個小實例,這三個小實例是我剛接觸vue的時候的練手做品,難度從很簡單到簡單,都是入門級的。但願能幫到你們更好的學習和了解vue,也是讓本身可以複習一下vue。若是發現文章寫得有什麼很差,寫錯了,或者有什麼建議!歡迎你們指點迷津!javascript
1.本篇文章使用的vue版本是2.4.2
,你們要注意版本問題
2.如今我也是假設您有基礎的html,css,javascript的知識,也已經看過了 官網的基本介紹,對vue有了一個大概的認識了,瞭解了經常使用的vue指令(v-model,v-show,v-if,v-for,v-on,v-bind等)!若是剛接觸前端的話,你看着文章可能會蒙圈,建議先學習基礎,掌握了基礎知識再來看!
3.下面的實例,建議你們邊看文章邊動手作!這樣思路會很是清晰,不易混亂!也不會以爲文章長!若是隻看文章,你可能未必會看完,由於文章我講得比較細,比較長!
4.這幾個實例,摘自我本身的日常練習的項目,代碼已經提到github上面了( vue-demos)。歡迎你們star。!
vue是如今很火的一個前端MVVM框架,它以數據驅動和組件化的思想構建,與angular和react並稱前端三大框架。相比angular和react,vue更加輕巧、高性能、也很容易上手。你們也能夠移步,看一下vue的介紹和核心功能官網介紹。簡單粗暴的理解就是:用vue開發的時候,就是操做數據,而後vue就會處理,以數據驅動去改變DOM(不知道有沒有理解錯,理解錯了指點下)。
下面就是一個最簡單的說明例子css
代碼以下html
html前端
<div id="app"> <p>{{ message }}</p> <input v-model="message"> </div>
jsvue
new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
相信也不難理解,就是input
綁定了message
這個值,而後在input
修改的時候,message
就改了,因爲雙向綁定,同時頁面的html({{ message }}
)進行了修改!
好,下面進入例子學習!html5
這個很簡單,無非就是一個點擊切換顯示而已。可是你們也要實現。若是這個看明白了,再看下面兩個!這個實例應該只是一個熱身和熟悉的做用!java
這個的步驟只有一步,原理也沒什麼。我直接在代碼打註釋,看了註釋,你們就明白了!react
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <style> body{ font-family:"Microsoft YaHei"; } #tab{ width: 600px; margin: 0 auto; } .tab-tit{ font-size: 0; width: 600px; } .tab-tit a{ display: inline-block; height: 40px; line-height: 40px; font-size: 16px; width: 25%; text-align: center; background: #ccc; color: #333; text-decoration: none; } .tab-tit .cur{ background: #09f; color: #fff; } .tab-con div{ border: 1px solid #ccc; height: 400px; padding-top: 20px; } </style> <body> <div id="tab"> <div class="tab-tit"> <!--點擊設置curId的值 若是curId等於0,第一個a添加cur類名,若是curId等於1,第二個a添加cur類名,以此類推。添加了cur類名,a就會改變樣式 @click,:class ,v-show這三個是vue經常使用的指令或添加事件的方式--> <a href="javascript:;" @click="curId=0" :class="{'cur':curId===0}">html</a> <a href="javascript:;" @click="curId=1" :class="{'cur':curId===1}">css</a> <a href="javascript:;" @click="curId=2" :class="{'cur':curId===2}">javascript</a> <a href="javascript:;" @click="curId=3" :class="{'cur':curId===3}">vue</a> </div> <div class="tab-con"> <!--根據curId的值顯示div,若是curId等於0,第一個div顯示,其它三個div不顯示。若是curId等於1,第二個div顯示,其它三個div不顯示。以此類推--> <div v-show="curId===0"> html<br/> </div> <div v-show="curId===1"> css </div> <div v-show="curId===2"> javascript </div> <div v-show="curId===3"> vue </div> </div> </div> </body> <script src="vue.min.js"></script> <script> new Vue({ el: '#tab', data: { curId: 0 }, computed: {}, methods: {}, mounted: function () { } }) </script> </html>
首先,仍是先把佈局寫好,和引入vue,準備vue實例,這個很少說,代碼以下webpack
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .fl{ float: left; } .fr{ float: right; } blockquote, body, dd, div, dl, dt, fieldset, form, h1, h2, h3, h4, h5, h6, img, input, li, ol, p, table, td, textarea, th, ul { margin: 0; padding: 0; } .clearfix{ zoom: 1; } .clearfix:after { clear: both; } .clearfix:after { content: '.'; display: block; overflow: hidden; visibility: hidden; font-size: 0; line-height: 0; width: 0; height: 0; } a{ text-decoration: none; color: #333; } img{vertical-align: middle;} .page-shopping-cart { width: 1200px; margin: 50px auto; font-size: 14px; border: 1px solid #e3e3e3; border-top: 2px solid #317ee7; } .page-shopping-cart .cart-title { color: #317ee7; font-size: 16px; text-align: left; padding-left: 20px; line-height: 68px; } .page-shopping-cart .red-text { color: #e94826; } .page-shopping-cart .check-span { display: block; width: 24px; height: 20px; background: url("shopping_cart.png") no-repeat 0 0; } .page-shopping-cart .check-span.check-true { background: url("shopping_cart.png") no-repeat 0 -22px; } .page-shopping-cart .td-check { width: 70px; } .page-shopping-cart .td-product { width: 460px; } .page-shopping-cart .td-num, .page-shopping-cart .td-price, .page-shopping-cart .td-total { width: 160px; } .page-shopping-cart .td-do { width: 150px; } .page-shopping-cart .cart-product-title { text-align: center; height: 38px; line-height: 38px; padding: 0 20px; background: #f7f7f7; border-top: 1px solid #e3e3e3; border-bottom: 1px solid #e3e3e3; } .page-shopping-cart .cart-product-title .td-product { text-align: center; font-size: 14px; } .page-shopping-cart .cart-product-title .td-check { text-align: left; } .page-shopping-cart .cart-product-title .td-check .check-span { margin: 9px 6px 0 0; } .page-shopping-cart .cart-product { padding: 0 20px; text-align: center; } .page-shopping-cart .cart-product table { width: 100%; text-align: center; font-size: 14px; } .page-shopping-cart .cart-product table td { padding: 20px 0; } .page-shopping-cart .cart-product table tr { border-bottom: 1px dashed #e3e3e3; } .page-shopping-cart .cart-product table tr:last-child { border-bottom: none; } .page-shopping-cart .cart-product table .product-num { border: 1px solid #e3e3e3; display: inline-block; text-align: center; } .page-shopping-cart .cart-product table .product-num .num-do { width: 24px; height: 28px; display: block; background: #f7f7f7; } .page-shopping-cart .cart-product table .product-num .num-reduce span { background: url("shopping_cart.png") no-repeat -40px -22px; display: block; width: 6px; height: 2px; margin: 13px auto 0 auto; } .page-shopping-cart .cart-product table .product-num .num-add span { background: url("shopping_cart.png") no-repeat -60px -22px; display: block; width: 8px; height: 8px; margin: 10px auto 0 auto; } .page-shopping-cart .cart-product table .product-num .num-input { width: 42px; height: 28px; line-height: 28px; border: none; text-align: center; } .page-shopping-cart .cart-product table .td-product { text-align: left; font-size: 12px; line-height: 20px; } .page-shopping-cart .cart-product table .td-product img { border: 1px solid #e3e3e3; margin-right: 10px; } .page-shopping-cart .cart-product table .td-product .product-info { display: inline-block; vertical-align: middle; } .page-shopping-cart .cart-product table .td-do { font-size: 12px; } .page-shopping-cart .cart-product-info { height: 50px; line-height: 50px; background: #f7f7f7; padding-left: 20px; } .page-shopping-cart .cart-product-info .delect-product { color: #666; } .page-shopping-cart .cart-product-info .delect-product span { display: inline-block; vertical-align: top; margin: 18px 8px 0 0; width: 13px; height: 15px; background: url("shopping_cart.png") no-repeat -60px 0; } .page-shopping-cart .cart-product-info .product-total { font-size: 14px; color: #e94826; } .page-shopping-cart .cart-product-info .product-total span { font-size: 20px; } .page-shopping-cart .cart-product-info .check-num { color: #333; } .page-shopping-cart .cart-product-info .check-num span { color: #e94826; } .page-shopping-cart .cart-product-info .keep-shopping { color: #666; margin-left: 40px; } .page-shopping-cart .cart-product-info .keep-shopping span { display: inline-block; vertical-align: top; margin: 18px 8px 0 0; width: 15px; height: 15px; background: url("shopping_cart.png") no-repeat -40px 0; } .page-shopping-cart .cart-product-info .btn-buy { height: 50px; color: #fff; font-size: 20px; display: block; width: 110px; background: #ff7700; text-align: center; margin-left: 30px; } .page-shopping-cart .cart-worder { padding: 20px; } .page-shopping-cart .cart-worder .choose-worder { color: #fff; display: block; background: #39e; width: 140px; height: 40px; line-height: 40px; border-radius: 4px; text-align: center; margin-right: 20px; } .page-shopping-cart .cart-worder .choose-worder span { display: inline-block; vertical-align: top; margin: 9px 10px 0 0; width: 22px; height: 22px; background: url("shopping_cart.png") no-repeat -92px 0; } .page-shopping-cart .cart-worder .worker-info { color: #666; } .page-shopping-cart .cart-worder .worker-info img { border-radius: 100%; margin-right: 10px; } .page-shopping-cart .cart-worder .worker-info span { color: #000; } .choose-worker-box { width: 620px; background: #fff; } .choose-worker-box .box-title { height: 40px; line-height: 40px; background: #F7F7F7; text-align: center; position: relative; font-size: 14px; } .choose-worker-box .box-title a { display: block; position: absolute; top: 15px; right: 16px; width: 10px; height: 10px; background: url("shopping_cart.png") no-repeat -80px 0; } .choose-worker-box .box-title a:hover { background: url("shopping_cart.png") no-repeat -80px -22px; } .choose-worker-box .worker-list { padding-top: 30px; height: 134px; overflow-y: auto; } .choose-worker-box .worker-list li { float: left; width: 25%; text-align: center; margin-bottom: 30px; } .choose-worker-box .worker-list li p { margin-top: 8px; } .choose-worker-box .worker-list li.cur a { color: #f70; } .choose-worker-box .worker-list li.cur a img { border: 1px solid #f70; } .choose-worker-box .worker-list li a:hover { color: #f70; } .choose-worker-box .worker-list li a:hover img { border: 1px solid #f70; } .choose-worker-box .worker-list li img { border: 1px solid #fff; border-radius: 100%; } </style> </head> <body> <div class="page-shopping-cart" id="shopping-cart"> <h4 class="cart-title">購物清單</h4> <div class="cart-product-title clearfix"> <div class="td-check fl"><span class="check-span fl check-all"></span>全選</div> <div class="td-product fl">商品</div> <div class="td-num fl">數量</div> <div class="td-price fl">單價(元)</div> <div class="td-total fl">金額(元)</div> <div class="td-do fl">操做</div> </div> <div class="cart-product clearfix"> <table> <tbody><tr> <td class="td-check"><span class="check-span"></span></td> <td class="td-product"><img src="testimg.jpg" width="98" height="98"> <div class="product-info"> <h6>【斯文】甘油 | 丙三醇</h6> <p>品牌:韓國skc 產地:韓國</p> <p>規格/純度:99.7% 起定量:215千克</p> <p>配送倉儲:上海倉海倉儲</p> </div> <div class="clearfix"></div> </td> <td class="td-num"> <div class="product-num"> <a href="javascript:;" class="num-reduce num-do fl"><span></span></a> <input type="text" class="num-input" value="3"> <a href="javascript:;" class="num-add num-do fr"><span></span></a> </div> </td> <td class="td-price"> <p class="red-text">¥<span class="price-text">800</span>.00</p> </td> <td class="td-total"> <p class="red-text">¥<span class="total-text">800</span>.00</p> </td> <td class="td-do"><a href="javascript:;" class="product-delect">刪除</a></td> </tr> <tr> <td class="td-check"><span class="check-span check-true"></span></td> <td class="td-product"><img src="testimg.jpg" width="98" height="98"> <div class="product-info"> <h6>【斯文】甘油 | 丙三醇</h6> <p>品牌:韓國skc 產地:韓國</p> <p>規格/純度:99.7% 起定量:215千克</p> <p>配送倉儲:上海倉海倉儲</p> </div> <div class="clearfix"></div> </td> <td class="td-num"> <div class="product-num"> <a href="javascript:;" class="num-reduce num-do fl"><span></span></a> <input type="text" class="num-input" value="1"> <a href="javascript:;" class="num-add num-do fr"><span></span></a> </div> </td> <td class="td-price"> <p class="red-text">¥<span class="price-text">800</span>.00</p> </td> <td class="td-total"> <p class="red-text">¥<span class="total-text">800</span>.00</p> </td> <td class="td-do"><a href="javascript:;" class="product-delect">刪除</a></td> </tr> </tbody></table> </div> <div class="cart-product-info"> <a class="delect-product" href="javascript:;"><span></span>刪除所選商品</a> <a class="keep-shopping" href="#"><span></span>繼續購物</a> <a class="btn-buy fr" href="javascript:;">去結算</a> <p class="fr product-total">¥<span>1600</span></p> <p class="fr check-num"><span>2</span>件商品總計(不含運費):</p> </div> <div class="cart-worder clearfix"> <a href="javascript:;" class="choose-worder fl"><span></span>綁定跟單員</a> <div class="worker-info fl"> </div> </div> </div> </body> <script src="vue.min.js"></script> <script> new Vue({ el:'#shopping-cart', data:{ }, computed: {}, methods:{ } }) </script> </html>
而後準備下列表數據,根據下面表格的箭頭css3
因此你們就知道嗎,下面的數據大概是漲這樣
productList:[ { 'pro_name': '【斯文】甘油 | 丙三醇',//產品名稱 'pro_brand': 'skc',//品牌名稱 'pro_place': '韓國',//產地 'pro_purity': '99.7%',//規格 'pro_min': "215千克",//最小起訂量 'pro_depot': '上海倉海倉儲',//所在倉庫 'pro_num': 3,//數量 'pro_img': '../../images/ucenter/testimg.jpg',//圖片連接 'pro_price': 800//單價 } ]
準備了這麼多,你們可能想到,還缺乏一個,就是記錄產品是否有選中,可是這個字段,雖然能夠在上面那裏加,可是意義不大,好比在日常項目那裏!後臺的數據不會這樣返回,數據庫也不會有這個字段,這個字段應該是本身添加的。代碼以下
new Vue({ el:'#shopping-cart', data:{ productList:[ { 'pro_name': '【斯文】甘油 | 丙三醇',//產品名稱 'pro_brand': 'skc',//品牌名稱 'pro_place': '韓國',//產地 'pro_purity': '99.7%',//規格 'pro_min': "215千克",//最小起訂量 'pro_depot': '上海倉海倉儲',//所在倉庫 'pro_num': 3,//數量 'pro_img': '../../images/ucenter/testimg.jpg',//圖片連接 'pro_price': 800//單價 } ] }, computed: {}, methods:{ }, mounted:function () { //爲productList添加select(是否選中)字段,初始值爲true var _this=this; //爲productList添加select(是否選中)字段,初始值爲true this.productList.map(function (item) { _this.$set(item, 'select', true); }) //要像上面這樣寫雙向綁定才能起效,下面的寫法是有問題的,雙向綁定不起效的! //this.productList.map(function (item) {item.select=true}) } })
爲了着重表示我修改了什麼地方,代碼我如今只貼出修改的部分,你們對着上面的佈局,就很容易知道我改的是什麼地方了!下面也是這樣操做!
點擊增長和減小按鈕(箭頭指向地方),所屬列的金額改變(紅框地方)
執行步驟1以前,要先把列表的數據給鋪出來。利用v-for指令。代碼以下
<tr v-for="item in productList"> <td class="td-check"><span class="check-span"></span></td> <td class="td-product"><img :src="item.pro_img" width="98" height="98"> <div class="product-info"> <h6>{{item.pro_name}}</h6> <p>品牌:{{item.pro_brand}} 產地:{{item.pro_place}}</p> <p>規格/純度:{{item.pro_purity}} 起定量:{{item.pro_min}}</p> <p>配送倉儲:{{item.pro_depot}}</p> </div> <div class="clearfix"></div> </td> <td class="td-num"> <div class="product-num"> <a href="javascript:;" class="num-reduce num-do fl" @click="item.pro_num--"><span></span></a> <input type="text" class="num-input" v-model="item.pro_num"> <a href="javascript:;" class="num-add num-do fr" @click="item.pro_num++"><span></span></a> </div> </td> <td class="td-price"> <p class="red-text">¥<span class="price-text">{{item.pro_price.toFixed(2)}}</span></p> </td> <td class="td-total"> <p class="red-text">¥<span class="total-text">{{item.pro_price*item.pro_num}}</span>.00</p> </td> <td class="td-do"><a href="javascript:;" class="product-delect">刪除</a></td> </tr>
這樣,列表的數據就有了!
也能夠發現,這兩個按鈕的功能已經實現了,後面的金額也會發生變化!是否是感到很驚喜!其實這裏沒什麼特別的,就是由於輸入框利用v-model綁定了數量(
pro_num
),而後兩個按鈕分別添加了事件@click="item.pro_num--"
和@click="item.pro_num++"
。好比剛開始pro_num是3,點擊,
pro_num
就變成2,點擊
,pro_num
就變成4,而後後面的金額會改改,是由於{{item.pro_price*item.pro_num}}
。只要pro_price或者pro_num的值改變了,整一塊也會改變,視圖就會刷新,咱們就能看到變化(這些事情是vue作的,這就是MVVM的魅力,數據驅動視圖改變)。
點擊所屬列選擇按鈕(箭頭指向地方),總計的金額(紅框地方)和已選產品的列數(藍框地方)和全選(黃框地方)會改變(若是已經全選了,全選按鈕自動變成全選,若是沒有全選,全選按鈕,自動取消全選)!
首先,選擇與取消選擇,在這裏只有兩個操做(其實只有一個:改變這條記錄的select
字段)。
而後改變,若是這條記錄
select
爲false
,就顯示,不然就顯示
。
代碼以下
<td class="td-check"><span class="check-span" @click="item.select=!item.select" :class="{'check-true':item.select}"></span></td>
其實就是等於添加了@click="item.select=!item.select" :class="{'check-true':item.select}"
這裏。點擊這個,這條數據的select
字段就取反(true->false或者false->true)。而後:class="{'check-true':item.select}"
,就會根據這條數據的select
字段進行判斷,是否添加check-true
類名,若是select
字段爲true,就添加類名,顯示。不然不添加類名,顯示
。
而後,全選按鈕,是否變成
。這裏用一個computed(計算屬性)就好。代碼以下
html
<div class="td-check fl"><span class="check-span fl check-all" :class="{'check-true':isSelectAll}"></span>全選</div>
js
computed: { isSelectAll:function(){ //若是productList中每一條數據的select都爲true,返回true,不然返回false; return this.productList.every(function (val) { return val.select}); } }
代碼我解釋下,就是計算屬性中,定義的isSelectAll依賴productList。只要productList改變,isSelectAll的返回值就會改變,而後:class="{'check-true':isSelectAll}"
根絕isSelectAll返回值是否添加'check-true'
類名,顯示對應的樣式!
最後,,這裏的多少件產品和總價,也是使用計算屬性,有了上一步的基礎,給出代碼,你們一看就明白了!
html
<p class="fr product-total">¥<span>{{getTotal.totalPrice}}</span></p> <p class="fr check-num"><span>{{getTotal.totalNum}}</span>件商品總計(不含運費):</p>
js
computed: { //檢測是否全選 isSelectAll:function(){ //若是productList中每一條數據的select都爲true,返回true,不然返回false; return this.productList.every(function (val) { return val.select}); }, //獲取總價和產品總件數 getTotal:function(){ //獲取productList中select爲true的數據。 var _proList=this.productList.filter(function (val) { return val.select}),totalPrice=0; for(var i=0,len=_proList.length;i<len;i++){ //總價累加 totalPrice+=_proList[i].pro_num*_proList[i].pro_price; } //選擇產品的件數就是_proList.length,總價就是totalPrice return {totalNum:_proList.length,totalPrice:totalPrice} } },
代碼很簡單,html根據getTotal返回值顯示數據,getTotal依賴productList的數據,只要productList改變,返回值會改變,視圖也會改變!
點擊全選按鈕(箭頭指向部分),會自動的對產品進行全選或者取消全選,下面的總計也會發生改變
作到這一步,你們應該知道,全選或者取消全選,就是改變記錄的select
。可是怎麼知道如今的列表有沒有全選呢?這個很賤,不須要在操做函數(全選與取消全選函數)裏面遍歷,你們應該還記得第二步的計算屬性isSelectAll
(爲true就是全選,不然不是全選),把這個傳進操做函數就好,而後操做函數,根據參數,決定執行全選,仍是取消全選操做。代碼以下!
html
<div class="td-check fl"><span class="check-span fl check-all" :class="{'check-true':isSelectAll}"@click="selectProduct(isSelectAll)"></span>全選</div>
js
methods: { //全選與取消全選 selectProduct:function(_isSelect){ //遍歷productList,所有取反 for (var i = 0, len = this.productList.length; i < len; i++) { this.productList[i].select = !_isSelect; } } },
點擊刪除產品,會刪除已經選中的,全選按鈕和下面的總計,都會變化!點擊每條記錄後面的刪除,會刪除當前的這條記錄。全選按鈕和下面的總計,也都會變化!
首先,點擊刪除產品,刪除已經選中。這個你們知道了怎麼作了!就是遍歷productList,若是哪條記錄的select爲true,就刪除。
而後,點擊每條記錄後面的刪除,刪除當前的這條記錄。這個在html遍歷productList的時候。順便帶上索引,而後把索引當成參數,傳進操做函數,而後根據索引參數,刪除productList的哪一條記錄。便可實現!代碼以下!
html
<!--遍歷的時候帶上索引--> <tr v-for="(item,index) in productList"> <td class="td-check"><span class="check-span" @click="item.select=!item.select" :class="{'check-true':item.select}"></span></td> <td class="td-product"><img :src="item.pro_img" width="98" height="98"> <div class="product-info"> <h6>{{item.pro_name}}</h6> <p>品牌:{{item.pro_brand}} 產地:{{item.pro_place}}</p> <p>規格/純度:{{item.pro_purity}} 起定量:{{item.pro_min}}</p> <p>配送倉儲:{{item.pro_depot}}</p> </div> <div class="clearfix"></div> </td> <td class="td-num"> <div class="product-num"> <a href="javascript:;" class="num-reduce num-do fl" @click="item.pro_num--"><span></span></a> <input type="text" class="num-input" v-model="item.pro_num"> <a href="javascript:;" class="num-add num-do fr" @click="item.pro_num++"><span></span></a> </div> </td> <td class="td-price"> <p class="red-text">¥<span class="price-text">{{item.pro_price.toFixed(2)}}</span></p> </td> <td class="td-total"> <p class="red-text">¥<span class="total-text">{{item.pro_price*item.pro_num}}</span>.00</p> </td> <td class="td-do"><a href="javascript:;" class="product-delect" @click="deleteOneProduct(index)">刪除</a></td> </tr> ... <a class="delect-product" href="javascript:;" @click="deleteProduct"><span></span>刪除所選商品</a>
js
//刪除已經選中(select=true)的產品 deleteProduct:function () { this.productList=this.productList.filter(function (item) {return !item.select}) }, //刪除單條產品 deleteOneProduct:function (index) { //根據索引刪除productList的記錄 this.productList.splice(index,1); },
樣式圖片
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .fl { float: left; } .fr { float: right; } blockquote, body, dd, div, dl, dt, fieldset, form, h1, h2, h3, h4, h5, h6, img, input, li, ol, p, table, td, textarea, th, ul { margin: 0; padding: 0; } .clearfix { zoom: 1; } .clearfix:after { clear: both; } .clearfix:after { content: '.'; display: block; overflow: hidden; visibility: hidden; font-size: 0; line-height: 0; width: 0; height: 0; } a { text-decoration: none; color: #333; } img { vertical-align: middle; } .page-shopping-cart { width: 1200px; margin: 50px auto; font-size: 14px; border: 1px solid #e3e3e3; border-top: 2px solid #317ee7; } .page-shopping-cart .cart-title { color: #317ee7; font-size: 16px; text-align: left; padding-left: 20px; line-height: 68px; } .page-shopping-cart .red-text { color: #e94826; } .page-shopping-cart .check-span { display: block; width: 24px; height: 20px; background: url("shopping_cart.png") no-repeat 0 0; } .page-shopping-cart .check-span.check-true { background: url("shopping_cart.png") no-repeat 0 -22px; } .page-shopping-cart .td-check { width: 70px; } .page-shopping-cart .td-product { width: 460px; } .page-shopping-cart .td-num, .page-shopping-cart .td-price, .page-shopping-cart .td-total { width: 160px; } .page-shopping-cart .td-do { width: 150px; } .page-shopping-cart .cart-product-title { text-align: center; height: 38px; line-height: 38px; padding: 0 20px; background: #f7f7f7; border-top: 1px solid #e3e3e3; border-bottom: 1px solid #e3e3e3; } .page-shopping-cart .cart-product-title .td-product { text-align: center; font-size: 14px; } .page-shopping-cart .cart-product-title .td-check { text-align: left; } .page-shopping-cart .cart-product-title .td-check .check-span { margin: 9px 6px 0 0; } .page-shopping-cart .cart-product { padding: 0 20px; text-align: center; } .page-shopping-cart .cart-product table { width: 100%; text-align: center; font-size: 14px; } .page-shopping-cart .cart-product table td { padding: 20px 0; } .page-shopping-cart .cart-product table tr { border-bottom: 1px dashed #e3e3e3; } .page-shopping-cart .cart-product table tr:last-child { border-bottom: none; } .page-shopping-cart .cart-product table .product-num { border: 1px solid #e3e3e3; display: inline-block; text-align: center; } .page-shopping-cart .cart-product table .product-num .num-do { width: 24px; height: 28px; display: block; background: #f7f7f7; } .page-shopping-cart .cart-product table .product-num .num-reduce span { background: url("shopping_cart.png") no-repeat -40px -22px; display: block; width: 6px; height: 2px; margin: 13px auto 0 auto; } .page-shopping-cart .cart-product table .product-num .num-add span { background: url("shopping_cart.png") no-repeat -60px -22px; display: block; width: 8px; height: 8px; margin: 10px auto 0 auto; } .page-shopping-cart .cart-product table .product-num .num-input { width: 42px; height: 28px; line-height: 28px; border: none; text-align: center; } .page-shopping-cart .cart-product table .td-product { text-align: left; font-size: 12px; line-height: 20px; } .page-shopping-cart .cart-product table .td-product img { border: 1px solid #e3e3e3; margin-right: 10px; } .page-shopping-cart .cart-product table .td-product .product-info { display: inline-block; vertical-align: middle; } .page-shopping-cart .cart-product table .td-do { font-size: 12px; } .page-shopping-cart .cart-product-info { height: 50px; line-height: 50px; background: #f7f7f7; padding-left: 20px; } .page-shopping-cart .cart-product-info .delect-product { color: #666; } .page-shopping-cart .cart-product-info .delect-product span { display: inline-block; vertical-align: top; margin: 18px 8px 0 0; width: 13px; height: 15px; background: url("shopping_cart.png") no-repeat -60px 0; } .page-shopping-cart .cart-product-info .product-total { font-size: 14px; color: #e94826; } .page-shopping-cart .cart-product-info .product-total span { font-size: 20px; } .page-shopping-cart .cart-product-info .check-num { color: #333; } .page-shopping-cart .cart-product-info .check-num span { color: #e94826; } .page-shopping-cart .cart-product-info .keep-shopping { color: #666; margin-left: 40px; } .page-shopping-cart .cart-product-info .keep-shopping span { display: inline-block; vertical-align: top; margin: 18px 8px 0 0; width: 15px; height: 15px; background: url("shopping_cart.png") no-repeat -40px 0; } .page-shopping-cart .cart-product-info .btn-buy { height: 50px; color: #fff; font-size: 20px; display: block; width: 110px; background: #ff7700; text-align: center; margin-left: 30px; } .page-shopping-cart .cart-worder { padding: 20px; } .page-shopping-cart .cart-worder .choose-worder { color: #fff; display: block; background: #39e; width: 140px; height: 40px; line-height: 40px; border-radius: 4px; text-align: center; margin-right: 20px; } .page-shopping-cart .cart-worder .choose-worder span { display: inline-block; vertical-align: top; margin: 9px 10px 0 0; width: 22px; height: 22px; background: url("shopping_cart.png") no-repeat -92px 0; } .page-shopping-cart .cart-worder .worker-info { color: #666; } .page-shopping-cart .cart-worder .worker-info img { border-radius: 100%; margin-right: 10px; } .page-shopping-cart .cart-worder .worker-info span { color: #000; } .choose-worker-box { width: 620px; background: #fff; } .choose-worker-box .box-title { height: 40px; line-height: 40px; background: #F7F7F7; text-align: center; position: relative; font-size: 14px; } .choose-worker-box .box-title a { display: block; position: absolute; top: 15px; right: 16px; width: 10px; height: 10px; background: url("shopping_cart.png") no-repeat -80px 0; } .choose-worker-box .box-title a:hover { background: url("shopping_cart.png") no-repeat -80px -22px; } .choose-worker-box .worker-list { padding-top: 30px; height: 134px; overflow-y: auto; } .choose-worker-box .worker-list li { float: left; width: 25%; text-align: center; margin-bottom: 30px; } .choose-worker-box .worker-list li p { margin-top: 8px; } .choose-worker-box .worker-list li.cur a { color: #f70; } .choose-worker-box .worker-list li.cur a img { border: 1px solid #f70; } .choose-worker-box .worker-list li a:hover { color: #f70; } .choose-worker-box .worker-list li a:hover img { border: 1px solid #f70; } .choose-worker-box .worker-list li img { border: 1px solid #fff; border-radius: 100%; } </style> </head> <body> <div class="page-shopping-cart" id="shopping-cart"> <h4 class="cart-title">購物清單</h4> <div class="cart-product-title clearfix"> <div class="td-check fl"><span class="check-span fl check-all" :class="{'check-true':isSelectAll}" @click="selectProduct(isSelectAll)"></span>全選</div> <div class="td-product fl">商品</div> <div class="td-num fl">數量</div> <div class="td-price fl">單價(元)</div> <div class="td-total fl">金額(元)</div> <div class="td-do fl">操做</div> </div> <div class="cart-product clearfix"> <table> <tbody> <!--遍歷的時候帶上索引--> <tr v-for="(item,index) in productList"> <td class="td-check"><span class="check-span" @click="item.select=!item.select" :class="{'check-true':item.select}"></span></td> <td class="td-product"><img :src="item.pro_img" width="98" height="98"> <div class="product-info"> <h6>{{item.pro_name}}</h6> <p>品牌:{{item.pro_brand}} 產地:{{item.pro_place}}</p> <p>規格/純度:{{item.pro_purity}} 起定量:{{item.pro_min}}</p> <p>配送倉儲:{{item.pro_depot}}</p> </div> <div class="clearfix"></div> </td> <td class="td-num"> <div class="product-num"> <a href="javascript:;" class="num-reduce num-do fl" @click="item.pro_num>0?item.pro_num--:''"><span></span></a> <input type="text" class="num-input" v-model="item.pro_num"> <a href="javascript:;" class="num-add num-do fr" @click="item.pro_num++"><span></span></a> </div> </td> <td class="td-price"> <p class="red-text">¥<span class="price-text">{{item.pro_price.toFixed(2)}}</span></p> </td> <td class="td-total"> <p class="red-text">¥<span class="total-text">{{item.pro_price*item.pro_num}}</span>.00</p> </td> <td class="td-do"><a href="javascript:;" class="product-delect" @click="deleteOneProduct(index)">刪除</a></td> </tr> </tbody> </table> </div> <div class="cart-product-info"> <a class="delect-product" href="javascript:;" @click="deleteProduct"><span></span>刪除所選商品</a> <a class="keep-shopping" href="#"><span></span>繼續購物</a> <a class="btn-buy fr" href="javascript:;">去結算</a> <p class="fr product-total">¥<span>{{getTotal.totalPrice}}</span></p> <p class="fr check-num"><span>{{getTotal.totalNum}}</span>件商品總計(不含運費):</p> </div> </div> </body> <script src="vue.min.js"></script> <script> new Vue({ el: '#shopping-cart', data: { productList: [ { 'pro_name': '【斯文】甘油 | 丙三醇',//產品名稱 'pro_brand': 'skc',//品牌名稱 'pro_place': '韓國',//產地 'pro_purity': '99.7%',//規格 'pro_min': "215千克",//最小起訂量 'pro_depot': '上海倉海倉儲',//所在倉庫 'pro_num': 3,//數量 'pro_img': '../../images/ucenter/testimg.jpg',//圖片連接 'pro_price': 800//單價 }, { 'pro_name': '【斯文】甘油 | 丙三醇',//產品名稱 'pro_brand': 'skc',//品牌名稱 'pro_place': '韓國',//產地 'pro_purity': '99.7%',//規格 'pro_min': "215千克",//最小起訂量 'pro_depot': '上海倉海倉儲',//所在倉庫 'pro_num': 3,//數量 'pro_img': '../../images/ucenter/testimg.jpg',//圖片連接 'pro_price': 800//單價 }, { 'pro_name': '【斯文】甘油 | 丙三醇',//產品名稱 'pro_brand': 'skc',//品牌名稱 'pro_place': '韓國',//產地 'pro_purity': '99.7%',//規格 'pro_min': "215千克",//最小起訂量 'pro_depot': '上海倉海倉儲',//所在倉庫 'pro_num': 3,//數量 'pro_img': '../../images/ucenter/testimg.jpg',//圖片連接 'pro_price': 800//單價 } ] }, computed: { //檢測是否全選 isSelectAll:function(){ //若是productList中每一條數據的select都爲true,返回true,不然返回false; return this.productList.every(function (val) { return val.select}); }, //獲取總價和產品總件數 getTotal:function(){ //獲取productList中select爲true的數據。 var _proList=this.productList.filter(function (val) { return val.select}),totalPrice=0; for(var i=0,len=_proList.length;i<len;i++){ //總價累加 totalPrice+=_proList[i].pro_num*_proList[i].pro_price; } //選擇產品的件數就是_proList.length,總價就是totalPrice return {totalNum:_proList.length,totalPrice:totalPrice} } }, methods: { //全選與取消全選 selectProduct:function(_isSelect){ //遍歷productList,所有取反 for (var i = 0, len = this.productList.length; i < len; i++) { this.productList[i].select = !_isSelect; } }, //刪除已經選中(select=true)的產品 deleteProduct:function () { this.productList=this.productList.filter(function (item) {return !item.select}) }, //刪除單條產品 deleteOneProduct:function (index) { //根據索引刪除productList的記錄 this.productList.splice(index,1); }, }, mounted: function () { var _this=this; //爲productList添加select(是否選中)字段,初始值爲true this.productList.map(function (item) { _this.$set(item, 'select', true); }) } }) </script> </html>
首先,仍是先把佈局寫好,和引入vue,準備vue實例,這個很少說,代碼以下
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> body{font-family: "微軟雅黑";font-size: 14px;} input{font-size: 14px;} body,ul,div,html{padding: 0;margin: 0;} .hidden{display: none;} .main{width: 800px;margin: 0 auto;} li{list-style-type: none;line-height: 40px;position: relative;border: 1px solid transparent;padding: 0 20px;} li .type-span{display: block;width: 10px;height: 10px;background: #ccc;margin: 14px 10px 0 0 ;float: left;} li .close{position: absolute;color: #f00;font-size: 20px;line-height: 40px;height: 40px;right: 20px;cursor: pointer;display: none;top: 0;} li:hover{border: 1px solid #09f;} li:hover .close{display: block;} li .text-keyword{height: 40px;padding-left: 10px;box-sizing: border-box;margin-left: 10px;width: 80%;display: none;} .text-keyword{box-sizing: border-box;width: 100%;height: 40px;padding-left: 10px;outline: none;} </style> </head> <body> <div id="app" class="main"> <h2>小目標列表</h2> <div class="list"> <h3>添加小目標</h3> <input type="text" class="text-keyword" placeholder="輸入小目標後,按回車確認"/> <p>共有N個目標</p> <p> <input type="radio" name="chooseType" checked="true"/><label>全部目標</label> <input type="radio" name="chooseType"/><label>已完成目標</label> <input type="radio" name="chooseType"/><label>未完成目標</label> </p> </div> <ul> <li class="li1"> <div> <span class="type-span"></span> <span>html5</span> <span class="close">X</span> </div> </li> <li class="li1"> <div> <span class="type-span"></span> <span>css3</span> <span class="close">X</span> </div> </li> </ul> </div> </body> <script src="vue2.4.2.js"></script> <script type="text/javascript"> new Vue({ el: "#app", data: { }, computed:{ }, methods:{ } }); </script> </html>
佈局有了,至關於一個骨架就有了,下面實現功能,一個一個來
輸入並回車,多一條記錄。下面的記錄文字也會改變
首先,大的輸入框回車要添加紀錄,那麼輸入框必須綁定一個值和一個添加紀錄的方法。
代碼以下:
而後,下面的記錄也要改變,因此,下面的記錄也要幫一個值,由於這個記錄可能會有多個,這個值就是一個數組,也能夠看到,記錄除了名稱,還有記錄是否完成的狀態,因此,綁定記錄的這個值確定是一個對象數組!代碼以下
最後,記錄文字要改變。這個只是一個當前記錄的長度便可!
爲了着重表示我修改了什麼地方,代碼我如今只貼出修改的部分,你們對着上面的佈局,就很容易知道我改的是什麼地方了!下面也是這樣操做!
html代碼
<!--利用v-model把addText綁定到input--> <input type="text" class="text-keyword" placeholder="輸入小目標後,按回車確認" @keyup.13='addList' v-model="addText"/> <p>共有{{prolist.length}}個目標</p> <!--v-for遍歷prolist--> <li class="li1" v-for="list in prolist"> <div> <span class="type-span"></span> <span>{{list.name}}</span> <span class="close">X</span> </div> </li>
js代碼
new Vue({ el: "#app", data: { addText:'', //name-名稱,status-完成狀態 prolist:[ {name:"HTML5",status:false}, {name:"CSS3",status:false}, {name:"vue",status:false}, {name:"react",status:false} ] }, computed:{ }, methods:{ addList(){ //添加進來默認status=false,就是未完成狀態 this.prolist.push({ name:this.addText, status:false }); //添加後,清空addText this.addText=""; } } });
測試一下,沒問題
點擊切換,下面記錄會改變
看到三個選項,也很簡單,無非就是三個選擇,一個是全部的目標,一個是全部已經完成的目標,一個是全部沒完成的目標。
首先.新建一個新的變量(newList),儲存prolist。遍歷的時候再也不遍歷prolist,而是遍歷newList。改變也是改變newList。
而後.選擇全部目標的時候,顯示所有prolist,把prolist賦值給newList。
而後.選擇全部已經完成目標的時候,只顯示prolist中,status爲true的目標,把prolist中,status爲true的項賦值給newList,
最後.選擇全部未完成目標的時候,只顯示status爲false的目標,把prolist中,status爲false的項賦值給newList。
代碼以下
html
<ul> <li class="li1" v-for="list in newList"> <div> <span class="status-span"></span> <span>{{list.name}}</span> <span class="close" @click='delectList(index)'>X</span> </div> </li> </ul>
js
new Vue({ el: "#app", data: { addText:'', //name-名稱,status-完成狀態 prolist:[ {name:"HTML5",status:false}, {name:"CSS3",status:false}, {name:"vue",status:false}, {name:"react",status:false} ], newList:[] }, computed:{ noend:function(){ return this.prolist.filter(function(item){ return !item.status }).length; } }, methods:{ addList(){ //添加進來默認status=false,就是未完成狀態 this.prolist.push({ name:this.addText, status:false }); //添加後,清空addText this.addText=""; }, chooseList(type){ //type=1時,選擇全部目標 //type=2時,選擇全部已完成目標 //type=3時,選擇全部未完成目標 switch(type){ case 1:this.newList=this.prolist;break; case 2:this.newList=this.prolist.filter(function(item){return item.status});break; case 3:this.newList=this.prolist.filter(function(item){return !item.status});break; } }, delectList(index){ //根據索引,刪除數組某一項 this.prolist.splice(index,1); //更新newList newList可能通過this.prolist.filter()賦值,這樣的話,刪除了prolist不會影響到newList 那麼就要手動更新newList this.newList=this.prolist; }, }, mounted(){ //初始化,把prolist賦值給newList。默認顯示全部目標 this.newList=this.prolist; } });
運行結果
紅色關閉標識,點擊會刪除該記錄。前面按鈕點擊會切換該記錄完成狀態,顏色也改變,記錄文字也跟着改變
首先點擊紅色關閉標識,點擊會刪除該記錄。這個應該沒什麼問題,就是刪除prolist的一條記錄!
而後前面按鈕點擊會切換該記錄完成狀態。這個也沒什麼,就是改變prolist的一條記錄的status字段!
最後記錄文字的改變,就是記錄prolist中status爲false的有多少條,prolist中status爲true的有多少條而已
html代碼
<!--若是noend等於0,就是所有完成了就顯示‘所有完成了’,若是沒有就是顯示已完成多少條(prolist.length-noend)和未完成多少條(noend)--> <p>共有{{prolist.length}}個目標,{{noend==0?"所有完成了":'已完成'+(prolist.length-noend)+',還有'+noend+'條未完成'}}</p> <ul> <li class="li1" v-for="(list,index) in newList"> <div> <span class="status-span" @click="list.status=!list.status" :class="{'status-end':list.status}"></span> <span>{{list.name}}</span> <span class="close" @click='delectList(index)'>X</span> </div> </li> </ul>
js
new Vue({ el: "#app", data: { addText:'', //name-名稱,status-完成狀態 prolist:[ {name:"HTML5",status:false}, {name:"CSS3",status:false}, {name:"vue",status:false}, {name:"react",status:false} ], newList:[] }, computed:{ //計算屬性,返回未完成目標的條數,就是數組裏面status=false的條數 noend:function(){ return this.prolist.filter(function(item){ return !item.status }).length; } }, methods:{ addList(){ //添加進來默認status=false,就是未完成狀態 this.prolist.push({ name:this.addText, status:false }); //添加後,清空addText this.addText=""; }, chooseList(type){ switch(type){ case 1:this.newList=this.prolist;break; case 2:this.newList=this.prolist.filter(function(item){return item.status});break; case 3:this.newList=this.prolist.filter(function(item){return !item.status});break; } }, delectList(index){ //根據索引,刪除數組某一項 this.prolist.splice(index,1); //更新newList newList可能通過this.prolist.filter()賦值,這樣的話,刪除了prolist不會影響到newList 那麼就要手動更新newList this.newList=this.prolist; }, }, mounted(){ this.newList=this.prolist; } });
運行結果
文字雙擊會出現輸入框,可輸入文字,若是回車或者失去焦點,就改變文字,若是按下ESC就恢復原來的文字
首先.雙擊出現輸入框,就是雙擊文字後,給當前的li設置一個類名(‘eidting
’),而後寫好樣式。當li出現這個類名的時候,就出現輸入框,而且隱藏其它內容。
而後.回車或者失去焦點,就改變文字這個只須要操做一個,就是把類名(‘eidting
’)清除掉。而後輸入框就會隱藏,其它內容顯示!
最後.按下ESC就恢復原來的文字,就是出現輸入框的時候,用一個變量(‘beforeEditText
’)先保存當前的內容,而後按下了ESC,就把變量(‘beforeEditText
’)賦值給當前操做的值!
代碼以下:
html
<ul> <li class="li1" v-for="(list,index) in newList" :class="{'eidting':curIndex===index}"> <div> <span class="status-span" @click="list.status=!list.status" :class="{'status-end':list.status}"></span> <span @dblclick="curIndex=index">{{list.name}}</span> <span class="close" @click='delectList(index)'>X</span> </div> <input type="text" class="text2" v-model='list.name' @keyup.esc='cancelEdit(list)' @blur='edited' @focus='editBefore(list.name)' @keyup.enter='edited'/> </li> </ul>
css(加上)
li div{display: block;} li.eidting div{display: none;} li .text2{height: 40px;padding-left: 10px;box-sizing: border-box;margin-left: 10px;width: 80%;display: none;} li.eidting .text2{display: block;}
js
methods:{ addList(){ //添加進來默認status=false,就是未完成狀態 this.prolist.push({ name:this.addText, status:false }); //添加後,清空addText this.addText=""; }, chooseList(type){ //type=1時,選擇全部目標 //type=2時,選擇全部已完成目標 //type=3時,選擇全部未完成目標 switch(type){ case 1:this.newList=this.prolist;break; case 2:this.newList=this.prolist.filter(function(item){return item.status});break; case 3:this.newList=this.prolist.filter(function(item){return !item.status});break; } }, delectList(index){ //根據索引,刪除數組某一項 this.prolist.splice(index,1); //更新newList newList可能通過this.prolist.filter()賦值,這樣的話,刪除了prolist不會影響到newList 那麼就要手動更新newList this.newList=this.prolist; }, //修改前 editBefore(name){ //先記錄當前項(好比這一項,{name:"HTML5",status:false}) //beforeEditText="HTML5" this.beforeEditText=name; }, //修改完成後 edited(){ //修改完了,設置curIndex="",這樣輸入框就隱藏,其它元素就會顯示。由於在li元素 寫了::class="{'eidting':curIndex===index}" 當curIndex不等於index時,eidting類名就清除了! //輸入框利用v-model綁定了當前項(好比這一項,{name:"HTML5",status:false})的name,當在輸入框編輯的時候,好比改爲‘HTML’,實際上當前項的name已經變成了‘HTML’,因此,這一步只是清除eidting類名,隱藏輸入框而已 //還有一個要注意的就是雖然li遍歷的是newList,好比改了newList的這一項({name:"HTML5",status:false}),好比改爲這樣({name:"HTML",status:true})。實際上prolist的這一項({name:"HTML5",status:false}),也會被改爲({name:"HTML",status:true})。由於這裏是一個對象,並且公用一個堆棧!修改其中一個,另外一個會被影響到 this.curIndex=""; }, //取消修改 cancelEdit(val){ //上面說了輸入框利用v-model綁定了當前項(好比這一項,{name:"HTML5",status:false})的name,當在輸入框編輯的時候,好比改爲‘HTML’,實際上當前項的name已經變成了‘HTML’,因此,這一步就是把以前保存的beforeEditText賦值給當前項的name屬性,起到一個恢復原來值得做用! val.name=this.beforeEditText; this.curIndex=""; } },
運行結果
還有一個小細節,你們可能注意到了,就是雙擊文字,出來輸入框的時候,還要本身手動點擊一下,才能得到焦點,咱們想雙擊了,輸入框出來的時候,自動獲取焦點,怎麼辦?自定義指令就好了!
computed:{...}, methods:{...}, mounted(){...}, directives:{ "focus":{ update(el){ el.focus(); } } }
而後html 調用指令
<input type="text" class="text2" v-model='list.name' @keyup.esc='cancelEdit(list)' @blur='edited' @focus='editBefore(list.name)' @keyup.enter='edited' v-focus/>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <style> body{font-family: "微軟雅黑";font-size: 14px;} input{font-size: 14px;} body,ul,div,html{padding: 0;margin: 0;} .hidden{display: none;} .main{width: 800px;margin: 0 auto;} li{list-style-type: none;line-height: 40px;position: relative;border: 1px solid transparent;padding: 0 20px;} li .status-span{display: block;width: 10px;height: 10px;background: #ccc;margin: 14px 10px 0 0 ;float: left;} li .status-span.status-end{ background: #09f; } li .close{position: absolute;color: #f00;font-size: 20px;line-height: 40px;height: 40px;right: 20px;cursor: pointer;display: none;top: 0;} li:hover{border: 1px solid #09f;} li:hover .close{display: block;} li div{display: block;} li.eidting div{display: none;} li .text2{height: 40px;padding-left: 10px;box-sizing: border-box;margin-left: 10px;width: 80%;display: none;} li.eidting .text2{display: block;} li .text-keyword{height: 40px;padding-left: 10px;box-sizing: border-box;margin-left: 10px;width: 80%;display: none;} .text-keyword{box-sizing: border-box;width: 100%;height: 40px;padding-left: 10px;outline: none;} </style> </head> <body> <div id="app" class="main"> <h2>小目標列表</h2> <div class="list"> <h3>添加小目標</h3> <input type="text" class="text-keyword" placeholder="輸入小目標後,按回車確認" @keyup.13='addList' v-model="addText"/> <!--若是noend等於0,就是所有完成了就顯示‘所有完成了’,若是沒有就是顯示已完成多少條(prolist.length-noend)和未完成多少條(noend)--> <p>共有{{prolist.length}}個目標,{{noend==0?"所有完成了":'已完成'+(prolist.length-noend)+',還有'+noend+'條未完成'}}</p> <p> <input type="radio" name="chooseType" checked="true" @click='chooseList(1)'/><label>全部目標</label> <input type="radio" name="chooseType" @click='chooseList(2)'/><label>已完成目標</label> <input type="radio" name="chooseType" @click='chooseList(3)'/><label>未完成目標</label> </p> </div> <ul> <li class="li1" v-for="(list,index) in newList" :class="{'eidting':curIndex===index}"> <div> <span class="status-span" @click="changeType(index)" :class="{'status-end':list.status}"></span> <span @dblclick="curIndex=index">{{list.name}}</span> <span class="close" @click='delectList(list)'>X</span> </div> <input type="text" class="text2" v-model='list.name' @keyup.esc='cancelEdit(list)' @blur='edited' @focus='editBefore(list.name)' @keyup.enter='edited' v-focus/> </li> </ul> </div> </body> <script src="vue.min.js"></script> <script type="text/javascript"> new Vue({ el: "#app", data: { addText:'', //name-名稱,status-完成狀態 prolist:[ {name:"HTML5",status:false}, {name:"CSS3",status:false}, {name:"vue",status:false}, {name:"react",status:false} ], newList:[], curIndex:'', beforeEditText:"", curType:0 }, computed:{ //計算屬性,返回未完成目標的條數,就是數組裏面status=false的條數 noend:function(){ return this.prolist.filter(function(item){ return !item.status }).length; } }, methods:{ addList(){ //添加進來默認status=false,就是未完成狀態 this.prolist.push({ name:this.addText, status:false }); //添加後,清空addText this.addText=""; }, chooseList(type){ //type=1時,選擇全部目標 //type=2時,選擇全部已完成目標 //type=3時,選擇全部未完成目標 this.curType=type; switch(type){ case 1:this.newList=this.prolist;break; case 2:this.newList=this.prolist.filter(function(item){return item.status});break; case 3:this.newList=this.prolist.filter(function(item){return !item.status});break; } }, /*改變單條數據的完成狀態*/ changeType(index){ this.newList[index].status=!this.newList[index].status; //更新數據 this.chooseList(this.curType); }, delectList(list){ var index=this.prolist.indexOf(list); //根據索引,刪除數組某一項 this.prolist.splice(index,1); //更新newList newList可能通過this.prolist.filter()賦值,這樣的話,刪除了prolist不會影響到newList 那麼就要手動更新newList //this.newList=this.prolist; this.chooseList(this.curType); }, //修改前 editBefore(name){ //先記錄當前項(好比這一項,{name:"HTML5",status:false}) //beforeEditText="HTML5" this.beforeEditText=name; }, //修改完成後 edited(){ //修改完了,設置curIndex="",這樣輸入框就隱藏,其它元素就會顯示。由於在li元素 寫了::class="{'eidting':curIndex===index}" 當curIndex不等於index時,eidting類名就清除了! //輸入框利用v-model綁定了當前項(好比這一項,{name:"HTML5",status:false})的name,當在輸入框編輯的時候,好比改爲‘HTML’,實際上當前項的name已經變成了‘HTML’,因此,這一步只是清除eidting類名,隱藏輸入框而已 //還有一個要注意的就是雖然li遍歷的是newList,好比改了newList的這一項({name:"HTML5",status:false}),好比改爲這樣({name:"HTML",status:true})。實際上prolist的這一項({name:"HTML5",status:false}),也會被改爲({name:"HTML",status:true})。由於這裏是一個對象,並且公用一個堆棧!修改其中一個,另外一個會被影響到 this.curIndex=""; }, //取消修改 cancelEdit(val){ //上面說了輸入框利用v-model綁定了當前項(好比這一項,{name:"HTML5",status:false})的name,當在輸入框編輯的時候,好比改爲‘HTML’,實際上當前項的name已經變成了‘HTML’,因此,這一步就是把以前保存的beforeEditText賦值給當前項的name屬性,起到一個恢復原來值得做用! val.name=this.beforeEditText; this.curIndex=""; } }, mounted(){ //初始化,把prolist賦值給newList。默認顯示全部目標 this.newList=this.prolist; }, directives:{ "focus":{ update(el){ el.focus(); } } } }); </script> </html>