每一粒尘埃

393 阅读4分钟

前端项目经验总结

UI组件

v-infinite-scroll 无限滚动

需求:需要设置一个列表,滚动到最底部请求额外数据

 <ul

      class="scroll"

      v-infinite-scroll="loadMoreData"

      infinite-scroll-immediate="false"

      infinite-scroll-distance="1"

      infinite-scroll-delay="300"

        :infinite-scroll-disabled="busy"

    >

通过v-infinite-scroll 每次滚动到最底部的时候触发对应的laodMoreData方法,

同时传入新的page,获取另外50条数据,再通过[...oldValue,...newValue]进行重赋值

 

Bug:

①如果不设置infinite-scroll-distance="1" 那么滚动到最底部的时候有且仅触发1次,或两次,之后滚动不会触发。

②如果不设置immeidate:false,那么会立刻触发一次

③需要进行一个节流,因为你设置distance:1后,滚动一次会自动触发两次,你要求300ms内有且仅能触发一次。注意!必须给滚动容器添加v-infinite-scroll不然会有bug


api

1、v-infinite-scroll="loadMore":表示回调函数是loadMore

2、infinite-scroll-disabled="busy":表示由变量busy决定是否执行loadMore,false则执行loadMore,true则不执行,注意,busy表示繁忙,繁忙的时候是不执行的。

2、infinite-scroll-distance="10":这里10决定了页面滚动到离页尾多少像素的时候触发回调函数,10是像素值。一般情况下会在页尾做一个几十像素高的“正在加载中...”,这样的话,可以把这个div的高度设为infinite-scroll-distance的值即可。

3、infinite-scroll-immediate:false:默认值为true,该指令表示,应该在绑定后立即检查busy的值和是否滚动到底。假如你的初始内容高度不够,不足以填满可滚动的容器的话,你应设为true,这样会立即执行一次loadMore,会帮你填充一些初始内容。

4、infinite-scroll-listen-for-event:当事件在Vue实例中发出时,无限滚动将再次检查。

5、infinite-scroll-delay:即节流  

 

自封装虚拟列表

1、计算出容器的容积,通过ref获取到容器,从而获取到高度 refs.xx.offsetHeight

2、计算出容器最大存放几条列表数据(前提是这个列表数据每条都必须相同,记录单条列表数据contentHeight+padding+border+margin),

3、

 

项目所遇

 

checkbox 复选与全选

有一个很重要的知识点:原生的checkbox是没法直接修改样式的,一般是模拟checkbox,或者直接使用UI组件库的

image.png

可以通过ant-desgin里的a-checkbox ,实现类似于checkbox的效果


实现全选  

//传入的值
     preference: {
        iciCode: {
          name: "iciCode",
          activeIndex: -1,
          indeterminate: true,
          list: [
            { label: "人工智能", value: "AI" },
            { label: "生物医药", value: "MAH" },
            { label: "虚拟现实", value: "MVS" },
            { label: "现代金融", value: "MF" },
            { label: "软件信息服务", value: "NGIT" },
            { label: "文化旅游", value: "CULT" },
            { label: "工业互联网及智能制造", value: "gyhlw" },
          ],
        },
//template 模板
      <a-checkbox
          :checked="allIsChecked(preference.iciCode.name)"
          @change="allNodeChange($event, preference.iciCode.name)"
          :indeterminate="preference.iciCode.indeterminate"
        >
          全部
        </a-checkbox>
        <a-checkbox
          v-for="(item, index) in preference.iciCode.list"
          :key="index"
          :checked="query.iciCode.includes(item.value)"
          @change="smallNodeChange($event, item.value, preference.iciCode.name)"
        >
          {{ item.label }}
        </a-checkbox>
        
  methods:{
        //小节点
    smallNodeChange(e, value, key) {
      if (e.target.checked) {
        this.query[key].push(value);
      } else {
        //过滤掉未选中项
        this.query[key] = this.query[key].filter((item) => item != value);
      }
      this.preference[key].indeterminate =
        !!this.query[key].length &&
        this.query[key].length < this.preference[key].list.length;
      console.log(this.query[key]);
    },
    //all
    allNodeChange(e, key) {
      this.preference[key].indeterminate = false;
      if (e.target.checked) {
        this.query[key] = this.preference[key].list.map((item) => item.value);
      } else {
        this.query[key] = [];
      }
      console.log(this.query[key]);
    },
    //全选样式
    allIsChecked(key) {
      return this.query[key].length == this.preference[key].list.length;
    },
}


        

  !!因为是多选,所以一般会用[]进行存储相对的value,这样可以通过includes判断某个复选框是否checked

//通过v-for来遍历list,获取到每一个对应的label和value,通过受控的选中数组.includes(value)来判断当前
项是否被选中
<a-checkbox v-for="(item, index) in preference.iciCode.list"
:key="index" 
:checked="query.iciCode.includes(item.value)" 
@change="smallNodeChange($event, item.value, preference.iciCode.name)" >


  通过change方法,进行修改。首先通过$event.target.checked,判断点击时是何状态,如果是选中, 那么直接往受控的选中数组里添加(includes就可以使得:checked=true,实现选中样式), 如果是非选中,那么过滤受控的选中数组即可~

Snipaste_2022-12-09_14-57-24.png  

Snipaste_2022-12-09_14-57-50.png  

v-for循环赋值不同的背景图

 <ul>

      <li

        v-for="(item, index) in InfoList"

        :key="item.id"

        class="listItem"

        :class="{

          [`bg${item.id}`]: true,

          [`activeBg${item.id}`]: index == activeIndex,

        }"

        @click="changeActive(index, item.name)"

      >

        <span>{{ item.text }}</span>

      </li>

    </ul>

 

 传入的时候需要将图片id与bg${id}对应
 .bg26 {
      background-image: url("../assets/images/yxgj.png");
      &:hover {
        background-image: url("../assets/images/active_yxgj.png");
      }
    }
    .activeBg1 {
      background-image: url("../assets/images/active_rgzn.png");
    }


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ecahrts图表

自定义数据项

场景:例如雷达图,我们自定义tooltip的时候,需要获取到axisName等属性,但是雷达图的formatter方法params参数没有对应的值,这时候我们可以根据series.data里的顺序额外定义参数,然后通过watch监听options配置项,传入到defaultOptions中,这样params.data中就可以获取到自定义属性

<template>
  <div
    :class="id == 'evalute-radar' ? 'radar-base' : 'radar-portrait'"
    :id="id"
    :style="{ height: height, width: width }"
  ></div>
</template>

<script>
import * as echarts from "echarts";

export default {
  props: {
    id: {
      type: String,
      default: "radar",
    },
    width: {
      type: String,
      default: "100%",
    },
    height: {
      type: String,
      default: "100%",
    },
    options: {
      type: Object,
      default: () => {
        return {
          indicator: [],
          seriesData: [],
        };
      },
    },
  },
  data() {
    return {
      styleType: 0,
      lineColor: ['#5BEBFF', '#FF9C5B'],
      areaColor: ['rgba(55,195,233,0.57)', 'rgba(233,104,55,0.57)'],
      myChart: null,
      defaultOptions: {
        color: ["#67F9D8"],
        tooltip: {
          trigger: "item",
          position: function (pos, params, dom, rect, size) {
            // 鼠标在左侧时 tooltip 显示到右侧,鼠标在右侧时 tooltip 显示到左侧。
            
            var obj = { top: 60 };
            obj[["left", "right"][+(pos[0] < size.viewSize[0] / 2)]] = 5;
            return obj;
          },
          backgroundColor: "#143d7b",
          borderColor: "#2d91c5",
          textStyle: {
            color: "#fff",
          },
          formatter: (params) => {
            let result = "<ul class='line-tool-tip'>";
            const { data, value } = params;
            const { text, max } = data;
            for (let i = 0; i < value.length; i++) {
              let node = `<div class='radar-item'>
                <span class="radar-tag" style="background-color: ${this.styleType == 1 ? this.lineColor[1] : this.lineColor[0]}"></span>
                ${text[i]}:${this.formatterNum(value[i], max[i])}分
                </div>`;
              result += node;
            }
            result += "</ul>";
            return result;
          },
        },
        // legend: {},
        radar: [
          {
            indicator: [],
            center: ["50%", "50%"],
            radius: 70, //维度各个点距离中心点的距离
            nameGap: 20, //维度名称距离中心点的距离
            startAngle: 90,
            splitNumber: 3,
            shape: "circle",
            axisName: {
              color: "#fff",
              fontsize: 16,
            },
            splitArea: {
              show: false,
              areaStyle: {
                color: ["#1b4f96", "#123c7e", "#1c4d9e"],
                shadowColor: "rgba(132, 188, 241, 1)",
                shadowBlur: 20,
              },
            },
            axisLine: {
              show: false,
              lineStyle: {
                color: "#ff0000",
                type: "dashed",
                width: 1.5,
              },
              symbol: ["none", "arrow"],
            },
            splitLine: {
              show: false,
              lineStyle: {
                color: ["#2160b5", "#2654ba", "#3d6eda"],
                shadowColor: "#1f5ba4",
                shadowBlur: 10,
                shadowOffsetX: -20,
                opacity: 0.6,
              },
            },
          },
        ],
        series: [
          {
            type: "radar",
            emphasis: {
              lineStyle: {
                width: 4,
              },
            },
            data: [
              {
                value: [],
                name: "",
                // lineStyle: {
                //   color: '#FF9C5B',
                //   width: 4
                // },
                itemStyle: {
                  color: '#FFFFFF'
                },
                // areaStyle: {
                //   color: "rgba(233,104,55,0.57)",
                // },
                text: [],
                max: [],
              },
            ],
          },
        ],
      },
    };
  },
  watch: {
    options: {
      immediate: true,
      handler: function (newValue) {
        if (newValue) {
          if (this.myChart) {
            this.myChart.dispose();
          }

          this.styleType = newValue.styleType;
          const max = [];
          const text = [];
          newValue.indicator.forEach((item) => {
            max.push(item.max);
            text.push(item.text);
          });
          this.defaultOptions.radar[0].indicator = newValue.indicator;
          this.defaultOptions.series[0].data[0].value = newValue.seriesData;
          this.defaultOptions.series[0].data[0].text = text;
          this.defaultOptions.series[0].data[0].max = max;
          this.defaultOptions.series[0].data[0].lineStyle = {
            width: 4,
            color:
              newValue.styleType == 1 ? this.lineColor[1] : this.lineColor[0],
          };
          this.defaultOptions.series[0].data[0].areaStyle = {
            color:
              newValue.styleType == 1 ? this.areaColor[1] : this.areaColor[0],
          };
          this.defaultOptions.tooltip.borderColor = newValue.styleType == 1 ? this.lineColor[1] : this.lineColor[0]
          this.$nextTick(() => {
            this.myChart = echarts.init(document.getElementById(this.id));
            this.myChart.setOption(this.defaultOptions);
          });
        }
      },
      deep: true,
    },
  },
  mounted() {
    window.addEventListener("resize", () => this.myChart.resize());
  },
  methods: {
    formatterNum(num, max) {
      return Math.round((num / max) * 100);
    },
  },
};
</script>


正常情况下,series的data中是没有text,max等属性的,可以自定义。再在watch中监听传入的值。

有个细节是newValue.xxx.forEach。这是因为如果有多条数据,是以数组形式进行传递的,可以给defaultOptions中配置多条数据,实现多个图表