Skip to content

vant 自定义组件

1. 自定义 loading

vue
<template>
  <van-loading class="diy-dialog" color="#1989fa" vertical style="">加载中...</van-loading>
</template>

<script>
export default {
  components: {},
  data() {
    return {};
  },
  props: {},
  computed: {},
  created() {},
  mounted() {},
  methods: {},
};
</script>

<style scoped lang="scss">
.diy-dialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.7);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
<template>
  <van-loading class="diy-dialog" color="#1989fa" vertical style="">加载中...</van-loading>
</template>

<script>
export default {
  components: {},
  data() {
    return {};
  },
  props: {},
  computed: {},
  created() {},
  mounted() {},
  methods: {},
};
</script>

<style scoped lang="scss">
.diy-dialog {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: rgba(0, 0, 0, 0.7);
  z-index: 9999;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

2. 自定义 nav-bar

vue
<template>
  <van-nav-bar
    :title="title || options.title"
    :border="options.border"
    left-arrow
    @click-left="goBack"
    :style="{
      '--var-title-color': options.titleColor,
      '--var-left-arrow-color': options.leftArrowColor,
      '--var-background-color': options.bgColor,
    }" />
</template>

<script>
import { NavBar } from 'vant';
export default {
  components: {
    [NavBar.name]: NavBar,
  },
  data() {
    return {};
  },
  props: {
    options: {
      type: Object,
      default: () => {
        return {
          border: true,
          bgColor: '#fff',
          titleColor: '#000',
          leftArrowColor: '#000',
        };
      },
    },
    title: {
      type: String,
      default: '',
    },
  },
  computed: {},
  created() {},
  mounted() {},
  methods: {
    goBack() {
      this.$router.go(-1);
    },
  },
};
</script>

<style scoped lang="scss">
.van-nav-bar {
  background-color: var(--var-background-color);
  ::v-deep .van-icon {
    color: var(--var-left-arrow-color);
  }
}
::v-deep .van-nav-bar__arrow {
  font-size: 20px;
}
::v-deep .van-nav-bar__title {
  font-size: 18px;
  color: var(--var-title-color);
}
</style>
<template>
  <van-nav-bar
    :title="title || options.title"
    :border="options.border"
    left-arrow
    @click-left="goBack"
    :style="{
      '--var-title-color': options.titleColor,
      '--var-left-arrow-color': options.leftArrowColor,
      '--var-background-color': options.bgColor,
    }" />
</template>

<script>
import { NavBar } from 'vant';
export default {
  components: {
    [NavBar.name]: NavBar,
  },
  data() {
    return {};
  },
  props: {
    options: {
      type: Object,
      default: () => {
        return {
          border: true,
          bgColor: '#fff',
          titleColor: '#000',
          leftArrowColor: '#000',
        };
      },
    },
    title: {
      type: String,
      default: '',
    },
  },
  computed: {},
  created() {},
  mounted() {},
  methods: {
    goBack() {
      this.$router.go(-1);
    },
  },
};
</script>

<style scoped lang="scss">
.van-nav-bar {
  background-color: var(--var-background-color);
  ::v-deep .van-icon {
    color: var(--var-left-arrow-color);
  }
}
::v-deep .van-nav-bar__arrow {
  font-size: 20px;
}
::v-deep .van-nav-bar__title {
  font-size: 18px;
  color: var(--var-title-color);
}
</style>

3. 自定义 tab-bar

vue
<template>
  <van-tabbar v-if="isShowTabbar" v-model="active" route>
    <van-tabbar-item
      v-for="(tabbar, index) in tabbars"
      :key="index"
      :name="tabbar.ename"
      replace
      :to="tabbar.path">
      <span>{{ tabbar.name }}</span>
      <template #icon="props">
        <img :src="props.active ? tabbar.activeIcon : tabbar.icon" />
      </template>
    </van-tabbar-item>
  </van-tabbar>
</template>

<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
  data() {
    return {
      active: 'supervise-unite',
      tabbars: [
        {
          name: '首页',
          activeIcon: require('./images/tabbar/icon_home_active.png'),
          icon: require('./images/tabbar/icon_home.png'),
          path: '/home',
          ename: 'home',
        },
        {
          name: '督办任务',
          activeIcon: require('./images/tabbar/icon_supervise_mission_active.png'),
          icon: require('./images/tabbar/icon_supervise_mission.png'),
          path: '/supervise-mission',
          ename: 'supervise-mission',
        },
        {
          name: '多督合一',
          activeIcon: require('./images/tabbar/icon_supervise_unite_active.png'),
          icon: require('./images/tabbar/icon_supervise_unite.png'),
          path: '/supervise-unite',
          ename: 'supervise-unite',
        },
        {
          name: '我的',
          activeIcon: require('./images/tabbar/icon_profile_active.png'),
          icon: require('./images/tabbar/icon_profile.png'),
          path: '/profile-page',
          ename: 'profile-page',
        },
      ],
    };
  },
  methods: {},
  created() {},
  computed: {
    isShowTabbar() {
      return ['/home', '/supervise-mission', '/supervise-unite', '/profile-page'].includes(
        this.$route.path
      );
    },
  },
};
</script>
<template>
  <van-tabbar v-if="isShowTabbar" v-model="active" route>
    <van-tabbar-item
      v-for="(tabbar, index) in tabbars"
      :key="index"
      :name="tabbar.ename"
      replace
      :to="tabbar.path">
      <span>{{ tabbar.name }}</span>
      <template #icon="props">
        <img :src="props.active ? tabbar.activeIcon : tabbar.icon" />
      </template>
    </van-tabbar-item>
  </van-tabbar>
</template>

<script>
import { Tabbar, TabbarItem } from 'vant';
export default {
  data() {
    return {
      active: 'supervise-unite',
      tabbars: [
        {
          name: '首页',
          activeIcon: require('./images/tabbar/icon_home_active.png'),
          icon: require('./images/tabbar/icon_home.png'),
          path: '/home',
          ename: 'home',
        },
        {
          name: '督办任务',
          activeIcon: require('./images/tabbar/icon_supervise_mission_active.png'),
          icon: require('./images/tabbar/icon_supervise_mission.png'),
          path: '/supervise-mission',
          ename: 'supervise-mission',
        },
        {
          name: '多督合一',
          activeIcon: require('./images/tabbar/icon_supervise_unite_active.png'),
          icon: require('./images/tabbar/icon_supervise_unite.png'),
          path: '/supervise-unite',
          ename: 'supervise-unite',
        },
        {
          name: '我的',
          activeIcon: require('./images/tabbar/icon_profile_active.png'),
          icon: require('./images/tabbar/icon_profile.png'),
          path: '/profile-page',
          ename: 'profile-page',
        },
      ],
    };
  },
  methods: {},
  created() {},
  computed: {
    isShowTabbar() {
      return ['/home', '/supervise-mission', '/supervise-unite', '/profile-page'].includes(
        this.$route.path
      );
    },
  },
};
</script>

4. 自定义下拉刷新上拉加载列表

vue
<template>
  <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :disabled="isRefreshDisable">
    <van-list
      class="lw-list"
      ref="vanList"
      v-model="loading"
      :finished="finished"
      finished-text="没有更多了"
      :immediate-check="false"
      @load="onLoad">
      <ListItem v-for="(item, index) in projects" :key="index" :item="item" />
    </van-list>
  </van-pull-refresh>
</template>

<script>
import ListItem from '../components/ListItem';
import { List, PullRefresh } from 'vant';

export default {
  data() {
    return {
      isRefreshDisable: false,
      projects: [],
      loading: false,
      finished: false,
      refreshing: false,
      scrollTop: 0,
    };
  },

  mounted() {
    this.$nextTick(() => {
      this.resolveConflict(this.$refs.vanList.$el);
    });
  },

  watch: {
    scrollTop(newval) {
      if (newval > 0) {
        this.isRefreshDisable = true;
      } else {
        this.isRefreshDisable = false;
      }
    },
  },

  methods: {
    // 解决van-pull-refresh下拉刷新与局部滚动条下滑冲突问题
    resolveConflict(val) {
      const vantList = val;
      vantList.addEventListener('touchmove', () => {
        this.scrollTop = vantList.scrollTop;
      });
      vantList.addEventListener('touchend', () => {
        this.scrollTop = vantList.scrollTop;
      });
    },

    async onLoad() {
      this.loading = true;

      if (this.roleList.includes('CBDW') && this.type === PERSONAL) {
        this.queryParams.queryObj.csldStatus = 1;
      }

      const { resultData } = await getDdhyTaskList(this.queryParams);

      if (this.refreshing) {
        this.projects = [];
        this.refreshing = false;
      }

      this.projects.push(...resultData.items);
      this.queryParams.skip += this.queryParams.pageSize;

      if (this.projects.length >= Number(resultData.total)) {
        this.finished = true;
      } else {
        this.finished = false;
      }

      // this.loading 得放在列表赋值的后面, 否则会导致上拉加载时, 加载所有数据
      this.loading = false;
    },

    onRefresh() {
      this.refreshing = true;
      this.loading = true;
      this.finished = false;
      this.queryParams.skip = 0;
      this.onLoad();
      this.$toast('刷新成功');
    },
  },
};
</script>
<template>
  <van-pull-refresh v-model="refreshing" @refresh="onRefresh" :disabled="isRefreshDisable">
    <van-list
      class="lw-list"
      ref="vanList"
      v-model="loading"
      :finished="finished"
      finished-text="没有更多了"
      :immediate-check="false"
      @load="onLoad">
      <ListItem v-for="(item, index) in projects" :key="index" :item="item" />
    </van-list>
  </van-pull-refresh>
</template>

<script>
import ListItem from '../components/ListItem';
import { List, PullRefresh } from 'vant';

export default {
  data() {
    return {
      isRefreshDisable: false,
      projects: [],
      loading: false,
      finished: false,
      refreshing: false,
      scrollTop: 0,
    };
  },

  mounted() {
    this.$nextTick(() => {
      this.resolveConflict(this.$refs.vanList.$el);
    });
  },

  watch: {
    scrollTop(newval) {
      if (newval > 0) {
        this.isRefreshDisable = true;
      } else {
        this.isRefreshDisable = false;
      }
    },
  },

  methods: {
    // 解决van-pull-refresh下拉刷新与局部滚动条下滑冲突问题
    resolveConflict(val) {
      const vantList = val;
      vantList.addEventListener('touchmove', () => {
        this.scrollTop = vantList.scrollTop;
      });
      vantList.addEventListener('touchend', () => {
        this.scrollTop = vantList.scrollTop;
      });
    },

    async onLoad() {
      this.loading = true;

      if (this.roleList.includes('CBDW') && this.type === PERSONAL) {
        this.queryParams.queryObj.csldStatus = 1;
      }

      const { resultData } = await getDdhyTaskList(this.queryParams);

      if (this.refreshing) {
        this.projects = [];
        this.refreshing = false;
      }

      this.projects.push(...resultData.items);
      this.queryParams.skip += this.queryParams.pageSize;

      if (this.projects.length >= Number(resultData.total)) {
        this.finished = true;
      } else {
        this.finished = false;
      }

      // this.loading 得放在列表赋值的后面, 否则会导致上拉加载时, 加载所有数据
      this.loading = false;
    },

    onRefresh() {
      this.refreshing = true;
      this.loading = true;
      this.finished = false;
      this.queryParams.skip = 0;
      this.onLoad();
      this.$toast('刷新成功');
    },
  },
};
</script>