整理常见组件封装 便于工作中使用

 

 

 

 

 

 

   

 

css核心布局:   如果纯是布局一分为四平均布局的话,只需要设父级为display: flex , 每个子元素设flex 1 ,便 可以平均支撑,

复制代码
.tab-bar{
  display: flex;     //核心布局方式核心
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  background: #999999;
box - shadow: 10px(x) 10px(y) 10px(模湖度) rgba 色() } .tab
-bar-itme{ flex: 1; //核心 text-align: center; line-height: 49px; }
复制代码

 初步封装好组件,在下为初步封装,测试效果

<template>
<div id="app">
<div class="tab-bar" >
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
<div class = "tabbar-item">首页</div>
</div>
</div>
</template>

组件封装好后,可以在一个新的文件调用:
复制代码
<template>
  <div id="app">
  <tabbar></tabbar>
  </div>
</template>
<script>
import tabbar from '@/components/tabbar'

export default {
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'about',
  components: {
    tabbar
  }
}
</script>
<style>
</style>
复制代码

通过上面封装只是看一下css和组件是不是能用,实际中组件中有图件有事件,需要一步步解决。
实际工作中菜单 上有图标,,而图标大小又不一定,加入图标后会布局不是想要的。先来解决css布局。

 

 版本1.0,没有考虚点击变色,核心理念是单独item 封成一个组件,外层定位是一个组件。下面红色文字有误, <div class="tab-bar-item">是定义外组件,说明里面有二个插巢,

 

 通过上面方法可以正确组件样子了,只是缺少点击变红色,激活状态的样式。想做出激活样式,需要插巢中有二个图标,动态选择显示

用if语句动态指定使用那个图标,对于文字可以使动用动态样式,

 插巢属性容易被替换,所以想动态绑定样式有点麻烦,需要外层加上div ;

解决了动态样式后,第三步需要解决跳转路由, 解决 方法,通过item绑定事件时,绑定在内部组件中,通过你组件传入要跳转路由

<template>
<div class ="tab-bar-item" @click = "itemClick">
  <div v-if="!isActive"><slot name = item-icon></slot></div>
  <div v-else><slot name = "item-icon-active"></slot></div>
  <div :class="{active: isActive}"><slot name = "item-text"></slot></div>
</div>
</template>
 
<script>
export default {
  name: 'tabberItem',
  // 这个组件跳转到那儿由父组件传过来
  props:{
    path: String
  },
  data(){
    return {
      isActive: true,
    }
  },
  methods: {
    itemClick(){
      // 点击时跳转 可以用$router.push,也可以用$router.replace
      this.$router.replace(this.path)
    }
  }
}
</script>
 
<style scoped>
 
</style>

  

 附注一下,通过父组件传入路由,关于路由模式,哈希模式是指/#/index 而history 模式则不带,直接/形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<template>
  <div id="app">
<!--    可以动态绑定path 也可以直接path="/home"-->
    <div class="tab-bar" path="/home">
      <div class = "tabbar-item">
        <img src="../../assets/img/tabbar/img.png" alt="">
        首页
      </div>
      <div class = "tabbar-item">
        <img src="../../assets/img/tabbar/img.png" alt="">
        首页
      </div>
      <div class = "tabbar-item">
        <img src="../../assets/img/tabbar/img.png" alt="">
        首页
      </div>
      <div class = "tabbar-item">
        <img src="../../assets/img/tabbar/img.png" alt="">
        首页
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
 
export default defineComponent({
  setup () {
    return {}
  }
})
 
</script>
<style>
.tab-bar{
  position: fixed;
  display: flex;
  bottom: 0;
  left: 0;
  right: 0;
  background-color: gainsboro;
}
.tabbar-item{
  flex: 1;
}
.tab-bar-item img{
  width: 24px;
  height: 24px;
  vertical-align: middle; //使图片居中一下,
}
</style>

 跳转路由解决了,还需要解决路由活跃时为高亮显示,解决思路是判断当时的路由是不是自己的一个路由值 相同,如果同样,就动态绑这个组件的动态style,判断这个值需要用到计算属性。

 

 如果styles样式要由外界传入,css样式不能写在组件中,也要写一个计算属性,计算如果是激活状态的话,样式为属性的某一个值,不是的话返回空

<div> :style="activeStyle"> <slot name="item-text"></slot></div>

由于activeStyle是由外界传入,所以需要props 接收,整 个底部导航功能到此结束

 

props:{
    path: String,
    activeColor:{
      type:String,
      default: 'red'
    }
  }

 activeStyle() { return this.isActive ? {color: this.activeColor} : {}   }

 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

 

 

导航菜单方式二,带返回简易功能形式

导航菜单有时有返回,主要用于主页,不同位置显示不一样,。左边有返回,中间有菜单 ,右边是个人中心,有时又只有一个菜单 ,这种菜单封装也
比较简单,

<div class="nav-bar">
<div class="left"><slot name="left"></slot></div>
<div class="left"><slot name="center"></slot></div>
<div class="left"><slot name="right"></slot></div>
</div>
////////////////////////////////////////////////////////////////
.nav-bar{
display: flex;
height: 44px;
line-height: 44px;
text-align: center;
box-shadow: 0 1px 1px rgba(100,100,100,.1);
}
.left, .right{
width: 60px;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

使用的时候直接按需求替换一个具名的插巢

<div id="home>"
<nav-bar class="home-nav"><div slot = "center">购物车</nav-bar>

这种功能的菜单和第一种方案其乎一样,简要说一下,说细参考第一方案,接来谈第三种方案,第三种更简单
主要是连插巢都不要了,通过外面传入数组显示菜单,增加一个路由跳转功能。

导航菜单形式三。

 

切换菜单请求组件:tobControl    给选项卡一样的控制栏 

 

 

复制代码
<template>
<!--  
  <div class="tab-control">
  <div v-for="item in titles">
    <span>{{item}}</span>
  </div>
  </div>
</template>
<script lang="ts">
export default {
  name: "tabControl",
  props: {
    titles: {
      type: Array,
      default(){
        return []
      }
    }
  }
复制代码

父组件使用的时候:   <tab-control :titles="['流行', '新款', '精选']">     

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<template>
<!--  导航栏,有些时候左边有图标,右边有也有,也些时候又没有,为了适应不同场景,定义三个具名插巢,使用的时候按名称替换-->
  <div class="tab-control">
  <div v-for="(item, index) in titles">
<!--   动态绑定css,使点击时切换激活css样式,然后需要编写一个函数切换点击后切换索引, -->
       class = "tab-control-item" :class="{active: index === currentIndex}" @click = "tabclick(index)">
    <span>{{item}}</span>
  </div>
  </div>
</template>
<script lang="ts">
export default {
  name: "tabControl",
  props: {
    titles: {
      type: Array,
      default(){
        return []
      }
    }
  },
  data(){
    return {
      currentIndex: 0 // 记录一个默契索引,用于点击时当前项变色
    }
  },
  methods:{
    tabclick(index){
      this.currentIndex =  index      点击时还要向外发送一个事件,告诉索引几被点击了      this.$emit('tabClck', index)??????????????????????????????????????????????????????????子组件向外发送事件和一个索引,父组件通过接收索引,在methods 方法中,用switch  case语法方法将索引转换成currenType父组件接收事件 @tabclick = "tabclick"类型。 父组件写法是  <good=list :goods="goods[currentType].list"上面的写法太长,太长的表达式尽量用计算属性转换一下                   <good-list :goods="showGoods"> 计算属性写法: computed:{showGoods(){return goods[currentType].list}}tabclick(index){switch(index){pop  case 0:  this.currentType =‘pop’  break   case 1:   this.currentType = "new"  break   case 2:   this.currentType = "sell"   如果有设defalt值的话,防旧case穿透,下面需要加上  break}}    }
  }
 
}
</script>
 
<style>
.tab-control{
  display: flex;
  text-align: center;
}
.tab-control-item{
  height: 40px;
  line-height: 40px;
  flex: 1;
}
/*主要是给itema项的spna 加一个空距,不要让下划线离太近*/
.tab-control-item span{
  padding: 4px;
}
.active{
  color: #FF0000;
 
}
/*给span加一个下划线,用户点击时高亮显示,*/
/*这个active由于是索引是点击时才会激活*/
.active span{
  border: 1px solid #CC0000;
}
</style>

  

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

附加功能       关健字    菜单停留

控制档菜单在下划拖动时不能离开屏幕,拖到顶部后到0后,就一直在那儿,实现思路一,先计算位置,当位置小于多少时,设置css属性

                                                                                                                         实现思路二,增加   position: sticky    css 

                                                                                                                                                         top:  44px

 

 

 

 

商品区菜单 封装    主要用于内容区,主要涉及到内容排列展示样式,和复杂数据请求处埋,滚动效果处理

??///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

第一步,请求内容,请求到的内容肯定要分类,先要一个大对象,大对象里面有三个小对象,由于数据太多,需要分页,所以小对象还要记录分页页码信息

请求时分别请求数据,并记录当前请求到的页码,下一次从下一页开始请求

 

1
2
3
4
5
// goods 放所有分类的数据,goods:{
    'pos': {page: 0, list:[]},
    'new': {page: 0, list:[]},
    'sell': {page: 0, list:[]},
  }

 

 在生命周期创建时发送请求数据。

created(){
this.getHomeGoods("pop")
this.getHomeGoods("new")
this.getHomeGoods("sell")
}

在正式说组件前先看一下我们要做的效果,心中有数才能更好表达。这儿有三个技术解决问题,布局平分,滚动效果,菜单路由处理 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////   

 

 

 

商品列表展示组件 
第一步,将获取到的数据传入组件展示。使用组件时向组件传入数据时,先清楚数据是什么样的格式,
这个数据格式一个大对象,里面有很多商品,每个商品又是一个小对象,有价格,图得等参数,所以需要拆分二层组件,一层名为所有商品,一层为
详细商品细节,

父组件(所有商品组件,也叫列表)
<goods :goods="goods['xxx'].list"/>
goods 商品组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<template>
<div class="goods">
  <goods-item v-for = "item in goods" :goods-item="item"/>
</div>
</template>
 
<script>
export default {
  name: 'Goods',
  components: {},
  props: {
    goods:{
      type: Array,
      default(){
        return []
      }
    }
  }
}
</script>
 
<style scoped>
 
</style>

  

详细商品item 组件(记录每个商品的价格,图片,等)
<template>
<div>
<div class="goods-item">
<img src="对象。图片" alt="">
<div class="goods-info">
<p>{{对象.描述}}</p>
<span class="price">{{对象.参数}}</span> <span class = "collect">{{对象.价格}}</span>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'goodsItem',
props:{
goodsitem:{
type:Object,
default () {
return {}
}
}
}
}
</script>

<style scoped>
/*本次对item 布局是先留个40px位置给padding占位,然后显示info的区别绝对定位*/
/*到个区域*/
.goods-item{
/*//这个40% 占位用于显示info内容*/
padding-bottom: 40px;
/*这个相对定位是给下面info区块定位参考,*/
position: relative;

//由于想让一列显示二个,所以设置每一个占48% 这个在留个空位 平分栏位需要父组件采用
// display: flex 平分布局
// flex-wrap: wrap 不有一行显示完
// justify-content:space-around 平分均等分
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.goods-item img{
  width: 100%;
  border-radius: 5px;
   
}
.goods-info{
  font-size: 12px;
  position:absolute;
  bottom: 5px;
  left: 0; right: 0;
  overflow: hidden;
  text-align: center;
}
.goods-info p{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-bottom: 3px;
}
.goods-info .price{
  color: #999999;
  margin-right: 20px;
}
.goods-info .collect{
  position:  relative;
}
.goods-info .collect::before{
  content: '';
  position: absolute;
  left: -15px;
  top: -1px;
  width: 14px;
  height: 14px;
  background: url("图标地址");
}
</style>

  

组件封装滚动简单示例,先看怎么使用,在逆向怎么封装,以下代码完成了监听滚动,和下拉到底提示,增加了一个按扭一健滚动到首部

复制代码
<template>
  <div>
    <div class="home">
      <div class=" text hiet">众油宝商城菜单</div>
      <b-scroll class="wrapper"
                ref='scroll'
                @scroll="fn"
                @pullingUp="pull"
                :probe-type="2"
      >
        <ul>
          <li>11111</li>
          <li>2222</li>
          <li>33333</li>
          cd
          <li>44444</li>
          <li>666666</li>
          <li>777777</li>
          <li>88888</li>
          <li>99999</li>
          <li>1111111</li>
          <li>aaaaaaaaaaaaaaaaaa</li>
          <li>ddddddddd</li>
          <li>rrrrrrrrrrrrrr</li>
          <li>aaaaaaaaaaaaa</li>
          <li>qqqqqqqqqqqqqqq</li>
          <li>bbbbbbbbbbbbbb</li>
          <li>jjjjjjjjjjjjjjj</li>
          <li>yyyyyyyyyyyyyyy</li>
          <li>oooooooooooooooo</li>
          <li>mmmmmmmmmmmmmmm</li>
          <li></li>
        </ul>
      </b-scroll>
      <back-top @click.native="contentScroll" v-show="isshow"></back-top>
      <div class="text bter">bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</div>
    </div>
  </div>
</template>
<script>
import bScroll from '../components/common/scroll/bScroll'
import backTop from '@/components/common/Topber/backTop'

export default {
  name: 'bIndex',
  components: {
    bScroll,
    backTop
  },
  data() {
    return {
      isshow: false
    }
  },
  methods: {
    contentScroll() {
      console.log('ol')
      // 方法一 this.$refs.scroll.scroll.scrollTo(0, 0, 500)
      this.$refs.scroll.scrollTo(0, 0, 500)
    },
    fn (position) {
      // fn是监听到滚动事件的处理函数,由于上下拉,y值为负,需要使用负号转正
      this.isshow = (-position.y) > 1000
      console.log(-position.y)
    },
    pull () {
      alert('拉到底了')
    }
  }
}
</script>

<style scoped>
.home {
  border: 2px solid #0074D9;
  height: 100vh;
}

.text {
  height: 45px;
  line-height: 45px;
  background: #7f7f7f;
}

.wrapper {
  width: 450px;
  height: calc(100% - 98px);
  border: #C21F39 1px solid;
  overflow: hidden;
}

ul {
  padding: 0;
  padding-left: 5px;
  width: 440px;
  height: auto;
  margin: 0 auto;
  border: 2px solid #C21F39;
  overflow: hidden;
}
li {
  list-style-type: none;
  float: left;
  margin: 2px;
  width: 210px;
  background: #999999;
  height: 350px;
}
</style>
复制代码

 

封装代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<template>
  <div ref="scr">
    <div class="content">
      <slot></slot>
    </div>
  </div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
  name: 'scroll',
  props: {
    // 定义滚动类型,0不滚动,本参数由外面父组件传入
    probeType: {
      type: Number,
      default: 0
    },
    // 定义是否支持支底通知,用于上拉加载
    pullUpLoad: {
      type: Boolean,
      defalut: false
    }
  },
  data() {
    return {
      scroll: null
    }
  },
  mounted () {
    // 监听和加载写在挂载生命周期,不能写在创建生命周期时,因为create时还没有加载dom
    this.scroll = new BScroll(this.$refs.scr, {
      click: true,
      probeType: this.probeType,
      pullUpLoad: true
    })
    // 监听滚动
    this.scroll.on('scroll', (position) => {
      this.$emit('scroll', position)
    })
    // 监听下拉加载
    this.scroll.on('pullingUp', () => {
      this.$emit('pullingUp')
    })
  },
  // 所有常用方法写在methods
  methods: {
    scrollTo (x, y, time = 500) {
      this.scroll.scrollTo(x, y, time)
    }
  }
}
</script>
<style scoped>
</style>

  

 

   









 

posted @   稷下元歌  阅读(156)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示