Vue利用flex布局实现TV端城市列表
vue中城市列表和搜索很常见,这篇博客就来说说咋实现搜索和城市列表
1.实现搜索布局代码:
<div class="search-bar">
  <input class="search-input" v-model="citySearchResult" :placeholder="searchDefault" :key="searchTitle"
         @endEditing="endEditing" :focusable="true" ref="searchInput" :duplicateParentState="true"
         :enableFocusBorder="true"/>
  <img class="index-root-search-image-view-css" :src="searchIcon">
  <span class="index-root-search-text-view-css" ref="textViewCity">{{searchDefaultKeyWord}}</span>
</div> 
2.搜索布局css样式代码:
.search-bar-root {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-top: 140px;
}
.index-root-search-title-css {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin-bottom: 40px;
}
.search-bar-root .search-bar {
  background-color: #ffffff;
  width: 1000px;
  height: 100px;
  display: flex;
  justify-content: center;
  border-radius: 8px;
}
.search-input {
  width: 780px;
  border-radius: 8px;
  font-size: 36px;
  font-family: PingFangSC-Regular, PingFang SC;
  font-weight: 400;
  color: #000000;
  margin-left: 40px;
  text-indent: 40px;
}
.index-root-search-image-view-css {
  position: absolute;
  width: 32px;
  height: 32px;
  top: 35px;
  bottom: 35px;
  right: 0;
  margin-right: 102px;
  text-align: center;
}
.index-root-search-flex-view-css {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  width: 1450px;
  margin-left: 245px;
  margin-right: 245px;
  margin-top: 40px;
}
.index-root-search-text-view-css {
  font-size: 30px;
  font-family: PingFangSC-Regular, PingFang SC;
  color: #000000;
  text-align: center;
  font-weight: 400;
  top: 0;
  bottom: 0;
  right: 0;
  position: absolute;
  margin-right: 30px;
}
.index-root-search-title-text-view-css {
  font-size: 70px;
  font-family: PingFangSC-Regular, PingFang SC;
  color: #ffffff;
  text-align: center;
  opacity: 1.0;
}
.search-city-button-view-css {
  width: 270px;
  height: 100px;
  background-color: rgba(0, 0, 0, .1);
  margin-right: 20px;
  margin-top: 40px;
  border-radius: 11px;
  border-width: 2px;
  border-color: rgba(255, 255, 255, 0.1);
  focus-background-color: #fff;
}
.search-city-button-view-css .city-sel-box {
  border-width: 2px;
  border-color: #32C5FF;
} 
3.城市列表布局代码:
<div class="index-root-search-flex-view-css" :clipChildren="false" ref="citySearch">
  <div class="search-city-button-view-css"
       v-for="(item,cityId) in hotCity" :focusable="true"
       :key="cityId"
       :ref="`hotRef${cityId}`" @focus="onFocus" :clipChildren="false">
    <div class="icon-location-reactive" ref="searchLocation" :duplicateParentState="true" style="visibility: visible;margin-right: 20px" v-if="showHot && cityId===0">
      <img src="@/assets/location.png" alt="" class="icon-location" showOnState="normal">
      <img src="@/assets/location_hot_focus.png" alt="" class="icon-location" showOnState="focused">
    </div>
    <span class="search-city-hot-text-iew-css" :duplicateParentState="true" showOnState="focused"
          ref="searchHotSelected"
          :style="{focusColor: focusHotTextColor,fontSize: textFontSize,fontWeight: textFontWeight,}">{{item.cityName}}</span>
  </div> 
4.城市列表css样式代码:
.index-root-search-flex-view-css {
  display: flex;
  flex-wrap: wrap;
  flex-direction: row;
  width: 1450px;
  margin-left: 245px;
  margin-right: 245px;
  margin-top: 40px;
}
.index-root-search-text-view-css {
  font-size: 30px;
  font-family: PingFangSC-Regular, PingFang SC;
  color: #000000;
  text-align: center;
  font-weight: 400;
  top: 0;
  bottom: 0;
  right: 0;
  position: absolute;
  margin-right: 30px;
}
.index-root-search-title-text-view-css {
  font-size: 70px;
  font-family: PingFangSC-Regular, PingFang SC;
  color: #ffffff;
  text-align: center;
  opacity: 1.0;
}
.search-city-button-view-css {
  width: 270px;
  height: 100px;
  background-color: rgba(0, 0, 0, .1);
  margin-right: 20px;
  margin-top: 40px;
  border-radius: 11px;
  border-width: 2px;
  border-color: rgba(255, 255, 255, 0.1);
  focus-background-color: #fff;
}
.search-city-button-view-css .city-sel-box {
  border-width: 2px;
  border-color: #32C5FF;
}
.icon-location-reactive {
  position: absolute;
  width: 26px;
  height: 34px;
  margin-left: 60px;
  margin-top: 30px;
  margin-bottom: 30px;
}
.icon-location {
  width: 26px;
  height: 34px;
  position: absolute;
  left: 0;
  top: 0;
  z-index: 9;
}
.search-city-hot-text-iew-css {
  width: 270px;
  height: 100px;
  background-color: rgba(50, 197, 255, 0.1);
  border-radius: 11px;
  border: 2px solid #32C5FF;
  font-size: 36px;
  font-family: PingFangSC-Regular, PingFang SC;
  text-align: center;
  color: white;
}
.search-city-empty {
  margin-top: 40px;
  width: 425px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  margin-left: 535px;
}
.search-city-empty .icon-no-connect {
  width: 425px;
  height: 307px;
}
.search-city-empty .empty-txt {
  font-size: 32px;
  font-family: PingFangSC-Light, PingFang SC;
  font-weight: 300;
  color: #FFFFFF;
  margin-top: 60px;
} 
5.城市列表获取焦点的事件:
主要是在div设置:focusable="true"和@focus="onFocus"
<div class="search-city-button-view-css"
             v-for="(item,cityId) in hotCity" :focusable="true"
             :key="cityId"
             :ref="`hotRef${cityId}`" @focus="onFocus" :clipChildren="false">
          <div class="icon-location-reactive" ref="searchLocation" :duplicateParentState="true" style="visibility: visible;margin-right: 20px" v-if="showHot && cityId===0">
            <img src="@/assets/location.png" alt="" class="icon-location" showOnState="normal">
            <img src="@/assets/location_hot_focus.png" alt="" class="icon-location" showOnState="focused">
 </div> 
6.设置焦点背景颜色和字体效果:
主要是设置:duplicateParentState="true"当文本获得焦点时颜色不受父布局影响,还可以设置焦点放大和带边框效果
:enableFocusBorder="true"//设置获得焦点时的边框 :focusScale="1.0"//设置焦点放大时的倍数
焦点效果的样式::style="{focusColor: focusHotTextColor,fontSize: textFontSize,fontWeight: textFontWeight,}
<span class="search-city-hot-text-iew-css" :duplicateParentState="true" showOnState="focused"
      ref="searchHotSelected"
      :style="{focusColor: focusHotTextColor,fontSize: textFontSize,fontWeight: textFontWeight,}">{{item.cityName}}</span> 
7.搜索框输入事件:
//输入内容之后请求城市列表接口刷新数据
endEditing(e) {
  console.log("--resultData--", this.citySearchResult)
}, 
8.搜索框获取焦点的事件:
onFocus(e) {
  this.focused = e.isFocused;
  this.$emit("onButtonFocused", e.isFocused);
}, 
9.默认弹出TV软键盘:
mounted() {
  this.hotCity = hotCity;
  this.showHot = true;
  this.pageLoading = true
  //弹出软键盘
  this.$refs.searchInput.focus()
  //搜索框默认获取焦点
  this.setHideLoading()
}, 
10.完整代码如下:
<template>
   <div class="index-root-search-view-css" :clipChildren="false">
     <img class="search-background-view-css" :src="searchImageData"/>
     <div class="search-bar-root">
       <div class="index-root-search-title-css">
         <span class="index-root-search-title-text-view-css"> {{ searchTitle }}</span>
       </div>
       <div class="search-bar">
         <input class="search-input" v-model="citySearchResult" :placeholder="searchDefault" :key="searchTitle"
                @endEditing="endEditing" :focusable="true" ref="searchInput" :duplicateParentState="true"
                :enableFocusBorder="true"/>
         <img class="index-root-search-image-view-css" :src="searchIcon">
         <span class="index-root-search-text-view-css" ref="textViewCity">{{searchDefaultKeyWord}}</span>
       </div>
       <div class="index-root-search-flex-view-css" :clipChildren="false" ref="citySearch">
         <div class="search-city-button-view-css"
              v-for="(item,cityId) in hotCity" :focusable="true"
              :key="cityId"
              :ref="`hotRef${cityId}`" @focus="onFocus" :clipChildren="false">
           <div class="icon-location-reactive" ref="searchLocation" :duplicateParentState="true" style="visibility: visible;margin-right: 20px" v-if="showHot && cityId===0">
             <img src="@/assets/location.png" alt="" class="icon-location" showOnState="normal">
             <img src="@/assets/location_hot_focus.png" alt="" class="icon-location" showOnState="focused">
           </div>
           <span class="search-city-hot-text-iew-css" :duplicateParentState="true" showOnState="focused"
                 ref="searchHotSelected"
                 :style="{focusColor: focusHotTextColor,fontSize: textFontSize,fontWeight: textFontWeight,}">{{item.cityName}}</span>
         </div>
         <div class="search-city-empty" v-if="hotCity.length === 0">
           <img src="@/assets/no_content.png" alt="" class="icon-no-connect"/>
           <p class="empty-txt">没有搜索结果~</p>
         </div>
       </div>
     </div>
     <loading-view
         style="width: 100px;height: 100px;position: absolute;left:960px;right:960px;top:500px;bottom:500px;align-self:
         center;align-items: center;justify-content: center" v-show="pageLoading"/>
   </div>
 </template>
<script>
 import searchImage from "@/assets/search_focus.png";
 import searchBackGroundImage from "@/assets/index-bg-qing.jpg";
 import {hotCity} from '@/views/contsants';
 import {ESLaunchManager} from "@extscreen/es-core";
export default {
   name: "city_list",
   props: {
     searchKeyWord: {
       type: String,
       default: '',
     },
     focusTextColor: {
       type: String,
       default: '#000000'
     },
     focusHotTextColor: {
       type: String,
       default: '#00EFFF'
     },
     textColor: {
       type: String,
       default: '#FFFFFF'
     },
     textFontSize: {
       type: String,
       default: '30px'
     },
     textFontWeight: {
       type: Number,
       default: 400
     },
     focusBackground: {
       orientation: 'TL_BR',//TOP_BOTTOM,TR_BL, RIGHT_LEFT, BR_TL, BOTTOM_TOP,BL_TR,LEFT_RIGHT,TL_BR,
       cornerRadius: [40, 40, 40, 40],
       normal: ['#00000000', '#00000000'],
       focused: ['#F5F5F5', '#F5F5F5'],
     },
   },
   data() {
     return {
       pageLoading: false,
       text: 'search city',
       search: '',
       searchIcon: searchImage,
       searchImageData: searchBackGroundImage,
       searchTitle: "切换城市",
       searchDefaultKeyWord: '搜索',
       searchDefault: '请输入城市名称首字母或全拼',
       focusColor: '#f5f5f5',
       citySearchResult: "",
       hotCity: [],
       cityName: "",
       cityId: "",
       showHot: true,
       params: '',
     }
   },
   activated() {
   },
   deactivated() {
     this.resetModel()
   },
   mounted() {
     this.hotCity = hotCity;
     this.showHot = true;
     this.pageLoading = true
     //弹出软键盘
     this.$refs.searchInput.focus()
     //搜索框默认获取焦点
     this.setHideLoading()
   },
   methods: {
     setHideLoading() {
       setTimeout(() => {
         this.pageLoading = false
       }, 500)
     },
     onFocus(e) {
       this.focused = e.isFocused;
       this.$emit("onButtonFocused", e.isFocused);
     },
     //输入内容之后请求城市
     endEditing(e) {
       console.log("--resultData--", this.citySearchResult)
     },
     onBackPressed() {
       ESLaunchManager.finishESPage();
     },
     resetModel() {
       this.citySearchResult = "";
       this.hotCity = [];
       this.pageLoading = false;
       this.searchTitle = "";
       this.searchDefaultKeyWord = "";
       this.searchDefault = "";
     },
   }
 }
 </script>
<style scoped>
 .index-root-search-view-css {
   width: 1920px;
   height: 1080px;
   background-color: transparent;
 }
.search-background-view-css {
   position: absolute;
   left: 0;
   top: 0;
   right: 0;
   bottom: 0;
   background-color: transparent;
 }
.search-bar-root {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   margin-top: 140px;
 }
.index-root-search-title-css {
   flex-direction: column;
   align-items: center;
   justify-content: center;
   margin-bottom: 40px;
 }
.search-bar-root .search-bar {
   background-color: #ffffff;
   width: 1000px;
   height: 100px;
   display: flex;
   justify-content: center;
   border-radius: 8px;
 }
.search-input {
   width: 780px;
   border-radius: 8px;
   font-size: 36px;
   font-family: PingFangSC-Regular, PingFang SC;
   font-weight: 400;
   color: #000000;
   margin-left: 40px;
   text-indent: 40px;
 }
.index-root-search-image-view-css {
   position: absolute;
   width: 32px;
   height: 32px;
   top: 35px;
   bottom: 35px;
   right: 0;
   margin-right: 102px;
   text-align: center;
 }
.index-root-search-flex-view-css {
   display: flex;
   flex-wrap: wrap;
   flex-direction: row;
   width: 1450px;
   margin-left: 245px;
   margin-right: 245px;
   margin-top: 40px;
 }
.index-root-search-text-view-css {
   font-size: 30px;
   font-family: PingFangSC-Regular, PingFang SC;
   color: #000000;
   text-align: center;
   font-weight: 400;
   top: 0;
   bottom: 0;
   right: 0;
   position: absolute;
   margin-right: 30px;
 }
.index-root-search-title-text-view-css {
   font-size: 70px;
   font-family: PingFangSC-Regular, PingFang SC;
   color: #ffffff;
   text-align: center;
   opacity: 1.0;
 }
.search-city-button-view-css {
   width: 270px;
   height: 100px;
   background-color: rgba(0, 0, 0, .1);
   margin-right: 20px;
   margin-top: 40px;
   border-radius: 11px;
   border-width: 2px;
   border-color: rgba(255, 255, 255, 0.1);
   focus-background-color: #fff;
 }
.search-city-button-view-css .city-sel-box {
   border-width: 2px;
   border-color: #32C5FF;
 }
.icon-location-reactive {
   position: absolute;
   width: 26px;
   height: 34px;
   margin-left: 60px;
   margin-top: 30px;
   margin-bottom: 30px;
 }
.icon-location {
   width: 26px;
   height: 34px;
   position: absolute;
   left: 0;
   top: 0;
   z-index: 9;
 }
.search-city-hot-text-iew-css {
   width: 270px;
   height: 100px;
   background-color: rgba(50, 197, 255, 0.1);
   border-radius: 11px;
   border: 2px solid #32C5FF;
   font-size: 36px;
   font-family: PingFangSC-Regular, PingFang SC;
   text-align: center;
   color: white;
 }
.search-city-empty {
   margin-top: 40px;
   width: 425px;
   display: flex;
   align-items: center;
   justify-content: center;
   flex-direction: column;
   margin-left: 535px;
 }
.search-city-empty .icon-no-connect {
   width: 425px;
   height: 307px;
 }
.search-city-empty .empty-txt {
   font-size: 32px;
   font-family: PingFangSC-Light, PingFang SC;
   font-weight: 300;
   color: #FFFFFF;
   margin-top: 60px;
 }
 </style>
11.实现的效果截图如下:

 



















