VantUI

安装

# Vue 3 项目,安装最新版 Vant
npm i vant

# Vue 2 项目,安装 Vant 2
npm i vant@latest-v2

引入组件

按需引入组件(推荐)

# 通过 npm 安装
npm i unplugin-vue-components -D

如果是基于 vite 的项目,在 vite.config.js 文件中配置插件:

import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
    vue(),
    Components({
      resolvers: [VantResolver()],
    }),
  ],
};

如果是基于 vue-cli 的项目,在 vue.config.js 文件中配置插件:

const { VantResolver } = require('unplugin-vue-components/resolvers');
const ComponentsPlugin = require('unplugin-vue-components/webpack');

module.exports = {
  configureWebpack: {
    plugins: [
      ComponentsPlugin({
        resolvers: [VantResolver()],
      }),
    ],
  },
};

如果是基于 webpack 的项目,在 webpack.config.js 文件中配置插件

const { VantResolver } = require('unplugin-vue-components/resolvers');
const ComponentsPlugin = require('unplugin-vue-components/webpack');

module.exports = {
  plugins: [
    ComponentsPlugin({
      resolvers: [VantResolver()],
    }),
  ],
};

缺少样式处理方案:

npm i -D babel-plugin-import

处理文件.babelrc

{
  ...,
  "plugins": [
    ...,
    [
      "import",
      {
        "libraryName": "vant",
        "libraryDirectory": "es",
        "style": true
      }
    ]
  ]
}

导入所有组件(不推荐)

Vant 支持一次性导入所有组件,引入所有组件会增加代码包体积,因此不推荐这种做法。

import { createApp } from 'vue';
import Vant from 'vant';
import 'vant/lib/index.css';

const app = createApp();
app.use(Vant);

手动按需引入组件(不推荐)

在不使用任何构建插件的情况下,可以手动引入需要使用的组件和样式。

// 引入组件脚本
import Button from 'vant/es/button/index';
// 引入组件样式
// 若组件没有样式文件,则无须引入
import 'vant/es/button/style/index';

修改Vant组件样式

reset.less

/* 添加这段样式后,Primary Button 会变成红色 */
html:root {
  --van-button-primary-background-color: red;
  --van-button-primary-border-color: red;
  ...
}
/* 其他重置羊水 */
html, body { margin: 0; padding: 0; }
body {
  overflow: hidden;
  background: #f6f7f8;
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
#app { overflow: auto; height: 100vh; }

main.ts

import './assets/reset.less'

局部修改 app.vue

<div :theme-vars="themeVars"></div>

<script>
import { ref } from 'vue';
export default {
  setup() {
    const rate = ref(4);
    const slider = ref(50);
    // themeVars 内的值会被转换成对应 CSS 变量
    // 比如 sliderBarHeight 会转换成 `--van-slider-bar-height`
    const themeVars = {
      rateIconFullColor: '#07c160',
      sliderBarHeight: '4px',
      sliderButtonWidth: '20px',
      sliderButtonHeight: '20px',
      sliderActiveBackgroundColor: '#07c160',
      buttonPrimaryBorderColor: '#07c160',
      buttonPrimaryBackgroundColor: '#07c160',
    };
    return {
      rate,
      slider,
      themeVars,
    };
  },
};
</script>

封装底部导航

footer.vue:底部全局组件

<template>
  <div class="footer">
    <div class="f-box" ref="FIXED" :style="{ backgroundColor: bgColor }">
      <slot />
    </div>
    <safe-inset-bottom />
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, onUnmounted, getCurrentInstance, } from "vue";

export default defineComponent({
  name: "MyFooter",
  props: {
    bgColor: { type: String, default: "#fff" },
  },
  setup() {
    const { proxy }: any = getCurrentInstance();
    const onStiatcHeight = () => {
      const isVisible = proxy.$route.meta.isVisibleFooterTab;
      const dom = proxy.$refs.FIXED;
      const H = dom && isVisible ? dom.offsetHeight : 0;
      proxy.$store.commit("SET_FOOTER_HEIGHT", `${H}px`);
    };

    onMounted(() => {
      onStiatcHeight();
      window.addEventListener("size", onStiatcHeight, false);
    });

    onUnmounted(() => {
      onStiatcHeight();
      window.removeEventListener("size", onStiatcHeight, false);
    });

    return {};
  },
});
</script>

footer-nav.vue:部分底部全局组件使用 这个是导航

<template>
  <transition name="fade">
    <MyFooter v-if="$route.meta.isVisibleFooterTab">
      <div class="ft-ul">
        <div class="ft-li">首页</div>
        <div class="ft-li">动态</div>
        <div class="ft-li">我的</div>
      </div>
    </MyFooter>
  </transition>
</template>

二次封装layout带下拉刷新

App.vue:整体的layout

<template>
  <my-layout>
    <router-view />
  </my-layout>
  <my-footer-nav />
</template>

新建文件refresh.ts

const content: any = [];

/*
  下拉刷新 触发 所有注册的函数
  @parmas callback // 关闭函数
  num // 计算执行是否最后一个才执行callback
*/
export const trigger = (callback: any) => {
  let num = content.length;
  content.forEach((item: any) => item(() => {
    num--;
    num <= 0 && callback()
  }))
}

// 收集所需的更新依赖
export const collect = (callback: any) => {
  content.push(callback)
}

// 切换路由beforeEach 触发清空操作
export const clear = () => {
  content.length = 0
}

layout.vue:封装下拉操作

<template>
  <van-pull-refresh
    class="refresh-layout"
    :disabled="$route.meta.isDisabledPull"
    v-model="loading"
    @refresh="onRefresh"
  >
    <slot />
    <div text="footer占位高度">
      <div :style="{ height: $store.state.footerHeight }" />
      <safe-inset-bottom />
    </div>
  </van-pull-refresh>
</template>

<script lang="ts">
import { defineComponent, ref } from "vue";
import { PullRefresh } from "vant";
import { trigger } from "@/tool/refresh";

export default defineComponent({
  name: "MyLayout",
  components: { [PullRefresh.name]: PullRefresh },
  setup() {
    const loading = ref(false);
    const onRefresh = () => {
      trigger(() => {
        setTimeout(() => {
          loading.value = false;
        }, 500);
      })
    };
    return { loading, onRefresh };
  },
});
</script>

<style>
.van-pull-refresh__track {
  min-height: 100vh;
}
</style>

A.vue:内部组件控制下拉刷新 完成操作

<script setup lang="ts">
import { collect } from "@/tool/refresh";

collect((close: any) => {
  const time = Math.random() * 3000
  setTimeout(() => { // 接口异步操作后  关闭
    close();
    console.log("child close", time);
  }, time);
});
</script>

二次封装向上滑动 加载更多

api.ts:模拟异步接口数据

// 模拟加载数据
export default function loadAPI(bodyparams: any) {
  const { pageSize: SS, pageNo: NN } = bodyparams;
  const total: number = 53;
  return new Promise((resovle) => {
    setTimeout(() => {
      const result: any = [];
      Array(SS)
        .fill(1)
        .forEach((u, x) => {
          const id = SS * (NN - 1) + (x + 1);
          id <= total &&
            result.push({
              id,
              code: Math.random().toString(32).substr(2),
              time: new Date().toString(),
            });
        });
      resovle({
        code: "000000",
        message: "ok",
        data: {
          pageNo: NN,
          list: result,
          total,
        },
      });
    }, Math.random() * 3000);
  });
}

MyList.vue

<template>
  <!-- 若 List 的内容使用了 float 布局,可以在容器上添加 van-clearfix 类名来清除浮动,使得 List 能正确判断元素位置。 -->
  <!-- 如果在 html 和 body 标签上设置了 overflow-x: hidden 样式,会导致 List 一直触发加载。 -->
  <van-list
    v-model:loading="isloading"
    :finished="isfinished"
    :offset="100"
    finished-text="没有更多了"
    @load="onLoad"
  >
    <div class="van-clearfix">
      <slot v-for="(u, x) in array" :key="rowKey ? u[rowKey] : x" :row="u" />
    </div>
  </van-list>
</template>

<script lang="ts">
import { computed, defineComponent, getCurrentInstance, ref } from "vue";
import { List } from "vant";
import loadAPI from "./api";


export default defineComponent({
  name: "MyList",
  components: { [List.name]: List },
  props: {
    action: String,  // 接口api
    rowKey: String,  // 指定遍历key的字段
    pageSize: { type: Number, default: 10 },        // 请求数量
    modelValue: { type: Array, default: () => [] }, // v-model值
    params: { type: Object, default: () => ({}) },  // 其它参数
  },
  emits: ["update:modelValue"], // v-model值update方法
  setup(props, cxt) {
    const array: any = computed({
      set(value) {
        cxt.emit("update:modelValue", value);
      },
      get() {
        return props.modelValue;
      },
    });

    let pageNo: number = 1;
    const isloading = ref(false);
    const isfinished = ref(false);

    const onLoad = () => {
      const data = {
        pageNo,
        pageSize: props.pageSize,
        ...props.params,
      };
      // this.$store.disatch(props.action, data).then((res: any) => {})
      return loadAPI(data).then((res: any) => {
        const { list = [], total } = res.data || {};
        array.value = [...array.value, ...list];
        pageNo++;
        isloading.value = false;
        isfinished.value = array.value.length >= total;
        return res;
      });
    };

    const onReload = () => {
      pageNo = 1;
      array.value = [];
      isloading.value = true;
      isfinished.value = false;
      return onLoad();
    };

    return {
      array,
      isloading,
      isfinished,
      onLoad,
      onReload,
    };
  },
});
</script>

A.vue:使用页面

<template>
  <my-list ref="T" v-model="info" rowKey="code" action="xxxxx">
    <template #default="{ row }">
      <div>{{ row }}</div>
    </template>
  </my-list>
</template>

<script setup lang="ts">
import { ref, getCurrentInstance } from "vue";
import { collect } from "@/tool/refresh";

const { proxy }: any = getCurrentInstance();
const info = ref([{}]);

collect((close: any) => {
  proxy.$refs.T.onReload().then((res: any) => {
    console.log('rrrr', res)
    close()
  });
})
</script>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值