??
標簽組件封裝
1.雙擊顯示,自動聚焦2.失去焦點,隱藏輸入框
標簽一列,不同行的標簽內容不同,但是除此之外其他基本一致,所以選擇用 標簽組件 將這一部分封裝為一個組件,需要時組件標簽展示。首先標簽處一進去就是顯示的 “茶具” 雙擊之后才顯示輸入框。所以輸入框和 “茶具”的顯示兩者是互斥的,用 v-if 和 v-else 來控制。
v-if 傳值為“isEdit” 如果為 false 就顯示 “茶具”,(最初定義的就是false,也就是一開始進去就是 “茶具”,什么時候會變為true繼而顯示輸入框呢),給v-else 也就是“茶具”所在的元素注冊一個雙擊事件,雙擊之后 isEdit由false變為true,就會顯示輸入框了。
接下來是聚焦,想要實現的是雙擊“茶具”出現輸入框之后就自動進行聚焦。開始做法是給輸入框dom元素添加ref屬性,在顯示輸入框之后this.isEdit=true,通過$refs立刻獲取焦點。但是vue是異步更新,輸入框顯示但是dom并沒有進行更新,所以出錯。這時,通過$nextTick檢測dom元素更新完成之后就立刻獲取焦點。
每次想要實現聚焦效果都要寫這一段代碼,很是麻煩,可以將這個效果進行指令封裝,并全局注冊,需要時直接添加指令即可。
輸入框和茶具不能一同顯示,兩個是互斥關系,用v-if和v-else
指令封裝,dom元素插入頁面自動聚焦
或者
輸入框失去焦點(顯示茶具)
App.vue<template><div class="table-case"><table class="my-table"><thead><tr><th>編號</th><th>名稱</th><th>圖片</th><th width="100px">標簽</th></tr></thead><tbody><tr><td>1</td><td>梨皮朱泥三絕清代小品壺經典款紫砂壺</td><td><img src="https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg" /></td><td><!-- 標簽組件 --><MyTag></MyTag></td></tr><tr><td>1</td><td>梨皮朱泥三絕清代小品壺經典款紫砂壺</td><td><img src="https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg" /></td><td><!-- 標簽組件 --><MyTag></MyTag></td></tr></tbody></table></div> </template><script> // my-tag 標簽組建的封裝 // 1.創建組件 - 初始化 // 2.實現功能 // (1)雙擊顯示,并且自動聚焦 // v-if v-else @dbclick // &nextTick =>$refs獲取到dom ,進行focus獲取焦點 // 封裝v-focus指令 // (2)失去焦點,隱藏輸入框 // (3)回顯標簽信息 // (4)內容修改了,回車 =》 修改標簽信息import MyTag from './components/MyTag.vue' export default {name: 'TableCase',components: {MyTag,},data() {return {goods: [{id: 101,picture:'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg',name: '梨皮朱泥三絕清代小品壺經典款紫砂壺',tag: '茶具',},{id: 102,picture:'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg',name: '全防水HABU旋鈕牛皮戶外徒步鞋山寧泰抗菌',tag: '男鞋',},{id: 103,picture:'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png',name: '毛茸茸小熊出沒,兒童羊羔絨背心73-90cm',tag: '兒童服飾',},{id: 104,picture:'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',name: '基礎百搭,兒童套頭針織毛衣1-9歲',tag: '兒童服飾',},],}}, } </script><style lang="less" scoped> .table-case {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}.my-table {width: 100%;border-spacing: 0;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}th {background: #f5f5f5;border-bottom: 2px solid #069;}td {border-bottom: 1px dashed #ccc;}td,th {text-align: center;padding: 10px;transition: all 0.5s;&.red {color: red;}}.none {height: 100px;line-height: 100px;color: #999;}}} </style>
MyTag.vue
<template> <div class="my-tag"><input v-if="isEdit"v-focusref="inp"class="input"type="text"placeholder="輸入標簽"@blur="isEdit = false"/><div v-else @dblclick="handleClick" class="text">茶具</div> </div> </template><script> export default {data(){return {isEdit:false,}},methods:{handleClick(){// 雙擊后切換到顯示狀態(輸入框)this.isEdit = true// // // 通過ref找到元素,立刻獲取焦點// // this.$refs.inp.focus()// // 異步更新,切換到顯示狀態后,dom并沒有進行更新,立即獲取焦點實際是獲取不到的// // 等dom更新王再獲取焦點// this.$nextTick(() => {// this.$refs.inp.focus()// })// // 每次都要點focus進行獲取焦點很麻煩,可以將這套指令封裝為指令,在main.js中進行全局注冊}}} </script><style lang="less" scoped>.my-tag {cursor: pointer;.input {appearance: none;outline: none;border: 1px solid #ccc;width: 100px;height: 40px;box-sizing: border-box;padding: 10px;color: #666;&::placeholder {color: #666;}}}</style>
main.js
3.回顯標簽信息4.內容修改了,回車 =>修改標簽信息
回顯的標簽信息是父組件傳遞過來的,在data中定義數據tempText,在子組件標簽中通過v-model幫綁定該數據,子組件中通過props接收該數據值value,顯示為還未雙擊之前的值,雙擊之后輸入框出現,顯示為輸入框回顯的值 :value="value"。修改輸入框中的內容回車之后希望顯示修改后的內容。也就是將修改后的數據傳送到父組件,父組件再傳送回子組件進行顯示。通過鍵盤回車綁定事件,將用戶輸入數據傳送回父組件,e.target能拿到事件源,也就是dom元素,e.target.value能拿到用戶輸入的值。
App.vue<template><div class="table-case"><table class="my-table"><thead><tr><th>編號</th><th>名稱</th><th>圖片</th><th width="100px">標簽</th></tr></thead><tbody><tr><td>1</td><td>梨皮朱泥三絕清代小品壺經典款紫砂壺</td><td><img src="https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg" /></td><td><!-- 標簽組件 --><MyTag v-model="tempText"></MyTag></td></tr><tr><td>1</td><td>梨皮朱泥三絕清代小品壺經典款紫砂壺</td><td><img src="https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg" /></td><td><!-- 標簽組件 --><MyTag></MyTag></td></tr></tbody></table></div> </template><script> // my-tag 標簽組建的封裝 // 1.創建組件 - 初始化 // 2.實現功能 // (1)雙擊顯示,并且自動聚焦 // v-if v-else @dbclick // &nextTick =>$refs獲取到dom ,進行focus獲取焦點 // 封裝v-focus指令 // (2)失去焦點,隱藏輸入框// (3)回顯標簽信息 // 回顯的標簽信息是父組件傳遞過來的 // v-model實現功能(簡化代碼) v-model => :value 和 @input(事件監聽) // 組件內部通過props接收,:value設置給輸入框 // (4)內容修改了,回車 =》 修改標簽信息 // @keyup.enter,觸發事件$emit('input',e.target.value)import MyTag from './components/MyTag.vue' export default {name: 'TableCase',components: {MyTag,},data() {return {// 測試組件功能臨時數據tempText:'茶壺', //希望子組件內容能跟這個數據進行雙向綁定,這里傳入啥,子組件就顯示啥;子組件回車又能修改到這里的數據goods: [{id: 101,picture:'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg',name: '梨皮朱泥三絕清代小品壺經典款紫砂壺',tag: '茶具',},{id: 102,picture:'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg',name: '全防水HABU旋鈕牛皮戶外徒步鞋山寧泰抗菌',tag: '男鞋',},{id: 103,picture:'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png',name: '毛茸茸小熊出沒,兒童羊羔絨背心73-90cm',tag: '兒童服飾',},{id: 104,picture:'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',name: '基礎百搭,兒童套頭針織毛衣1-9歲',tag: '兒童服飾',},],}}, } </script><style lang="less" scoped> .table-case {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}.my-table {width: 100%;border-spacing: 0;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}th {background: #f5f5f5;border-bottom: 2px solid #069;}td {border-bottom: 1px dashed #ccc;}td,th {text-align: center;padding: 10px;transition: all 0.5s;&.red {color: red;}}.none {height: 100px;line-height: 100px;color: #999;}}} </style>
MyTag.vue
<template> <div class="my-tag"><!-- v-model不能跟父組件傳遞過來的數據進行直接綁定,一旦綁定,就相當于直接在修改父組件傳遞過來的數據,子組件泵你直接修改父組件傳遞過來的數據 --><!-- :value="value"相當于父組件傳遞過來什么值,子組件就顯示什么值并進行綁定,該值就回顯了 --><input v-if="isEdit"v-focusref="inp"class="input"type="text"placeholder="輸入標簽"@blur="isEdit = false":value="value"@keyup.enter="handleEnter"/><div v-else @dblclick="handleClick" class="text"><!-- 茶具 -->{{ value }}</div> </div> </template><script> export default {props:{value:String,},data(){return {isEdit:false,}},methods:{handleClick(){// 雙擊后切換到顯示狀態(輸入框)this.isEdit = true// // // 通過ref找到元素,立刻獲取焦點// // this.$refs.inp.focus()// // 異步更新,切換到顯示狀態后,dom并沒有進行更新,立即獲取焦點實際是獲取不到的// // 等dom更新王再獲取焦點// this.$nextTick(() => {// this.$refs.inp.focus()// })// // 每次都要點focus進行獲取焦點很麻煩,可以將這套指令封裝為指令,在main.js中進行全局注冊},handleEnter(e){// 非空處理if (e.target.value.trim() === '') return alert('標簽不能為空')// 需要子傳父,回車時,輸入框的內容提交給父組件更新,// 父組件是v-model,觸發事件,需要觸發input事件this.$emit('input',e.target.value)// 或者this.$refs.inp.value// e.target拿到的是觸發事件的事件源,也就是dom元素,想拿到輸入框輸入的值,直接.value就是了// 提交完成,關閉輸入狀態}}} </script><style lang="less" scoped>.my-tag {cursor: pointer;.input {appearance: none;outline: none;border: 1px solid #ccc;width: 100px;height: 40px;box-sizing: border-box;padding: 10px;color: #666;&::placeholder {color: #666;}}}</style>
my-table表格組件封裝
表格中表頭和中間體不能寫死,類似的表格還有很多,可以復用。
中間體:表格組件中接收父組件傳過來的數據,進行遍歷顯示。因為該部分不能固定著,以便將來能復用,所以將該部分td表格中的內容用slot具名插槽進行占位,并將td表格中內容剪切到父組件的表格組件標簽中,用template標簽接收插槽名。還因為該部分遍歷的數據item和index在表格組件里,所以需要用slot以屬性的方式將這兩個數據打包成對象傳到父組件,父組件直接解構接收。
表頭:直接將標頭的內容剪切到父組件的子組件標簽中,并在子組件中使用具名插槽占位
并且在表格組件標簽的“標簽”一列顯示 MyTag標簽組件標簽,將其v-model綁定的數據改為item中的tagApp.vue
<template><div class="table-case"><MyTable :data="goods"><tempalte :slot="head"><th>>編號</th><th>>圖片</th><th>>名稱</th><th width="100px">標簽</th></tempalte><template #body="{item,index}"><td>{{ index+1 }}</td><td>{{ item.name }}</td><td><img :src="item.picture" /></td><td><!-- 標簽組件 --><MyTag v-model="item.tag"></MyTag></td></template></MyTable></div> </template><script> // my-tag 標簽組建的封裝 // 1.創建組件 - 初始化 // 2.實現功能 // (1)雙擊顯示,并且自動聚焦 // v-if v-else @dbclick // &nextTick =>$refs獲取到dom ,進行focus獲取焦點 // 封裝v-focus指令 // (2)失去焦點,隱藏輸入框// (3)回顯標簽信息// 回顯的標簽信息是父組件傳遞過來// v-moedl實現功能(簡化代碼) v-model => :value 和 @input// 組件內部通過props接收,:value設置給輸入框 // (4)內容修改了,回車 =》 修改標簽信息// @keyup.enter,觸發事件$emit('input',r.target.value)// my-tag 表格組件的封裝 // 1.數據不能寫死,動態傳遞表格渲染的數據 // 2.結構不能寫死 - 多處結構自定義// (1)表頭支持自定義// (2)主體支持自定義 import MyTag from './components/MyTag.vue' import MyTable from './components/MyTable.vue'export default {// 測試組件功能臨時文本tempText:'茶壺',name: 'TableCase',components: {MyTag,MyTable,},data() {return {goods: [{id: 101,picture:'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg',name: '梨皮朱泥三絕清代小品壺經典款紫砂壺',tag: '茶具',},{id: 102,picture:'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg',name: '全防水HABU旋鈕牛皮戶外徒步鞋山寧泰抗菌',tag: '男鞋',},{id: 103,picture:'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png',name: '毛茸茸小熊出沒,兒童羊羔絨背心73-90cm',tag: '兒童服飾',},{id: 104,picture:'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',name: '基礎百搭,兒童套頭針織毛衣1-9歲',tag: '兒童服飾',},],}}, } </script><style lang="less" scoped> .table-case {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}} </style>
MyTable.vue
<template><table class="my-table"><thead><tr><slot name="head"></slot></tr></thead><tbody><tr v-for="(item,index) in data" :key="item.di"><slot :item="item" :index="index"></slot></tr> </tbody></table> </template><script> export default {props:{data:{type:Array,required:true,}} } </script><style lang="less">.my-table {width: 100%;border-spacing: 0;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}th {background: #f5f5f5;border-bottom: 2px solid #069;}td {border-bottom: 1px dashed #ccc;}td,th {text-align: center;padding: 10px;transition: all 0.5s;&.red {color: red;}}.none {height: 100px;line-height: 100px;color: #999;}}</style>
MyTag.vue
<template><div class="my-tag"><input v-if="isEdit"v-focusref="inp"class="input"type="text"placeholder="輸入標簽":value="value"@blur="isEdit = false"@keyup.enter="handleEnter"/><div v-else @dblclick="handleClick" class="text"><!-- 茶具 -->{{ value }}</div></div></template><script>export default {props:{value:String},data(){return {isEdit:false,}},methods:{handleClick(){// 雙擊后切換到顯示狀態(輸入框)this.isEdit = true// // // 通過ref找到元素,立刻獲取焦點// // this.$refs.inp.focus()// // 異步更新,切換到顯示狀態后,dom并沒有進行更新,立即獲取焦點實際是獲取不到的// // 等dom更新王再獲取焦點// this.$nextTick(() => {// this.$refs.inp.focus()// })// // 每次都要點focus進行獲取焦點很麻煩,可以將這套指令封裝為指令,在main.js中進行全局注冊},handleEnter(e){if (e.target.value.trim() === '') return alert('標簽不能為空')// 子傳父,將回車時,輸入框的內容提交給父組件更新// 由于父組件是v-model,所以觸發事件需要觸發input事件this.$emit('input',e.target.value)// 提交完成,關閉輸入狀態this.isEdit = false}}}</script><style lang="less" scoped>.my-tag {cursor: pointer;.input {appearance: none;outline: none;border: 1px solid #ccc;width: 100px;height: 40px;box-sizing: border-box;padding: 10px;color: #666;&::placeholder {color: #666;}}}</style>
補充:在輸入框中的? :value = "value",是指將父組件通過v-model傳過來的value值綁定給輸入框自定義的屬性 :value。后面e.target.value也就是獲取元素對象屬性value的值。