分类
svg

数据大屏边框(1)

起因

通常,我们做一些数据大屏项目时会使用一些插件来完成特效的制作,比如DataV组件库提供了许多大屏的特效。但是这样ui就不能自由自在的创作了,为了解决此问题,参照着DataV的写法,试试能否实现ui的设计稿。

ui设计稿

ui sj 1024x654 - 数据大屏边框(1)
ui画的设计稿。

思路

  • 左上角有个小三角。
  • 外框是有path组成。
  • 右侧的小方块一个一个画吧。
  • 该边框形成一个特定的比例。
  • 看左侧和下侧还有光晕,是动画吧。

开搞

定义一个特定的比例
<svg
            class="ec-svg-container"
            width="697"
            height="462"
            viewBox="0 0 697 462"
            xmlns="http://www.w3.org/2000/svg"
    >...</svg>
左上角小三角
<path d="M 5 5 L 20 5 L 5 20 z"
              fill="transparent" stroke-linejoin="round" stroke="rgba(19,101,233)" stroke-width="2" />
外框
 <path d="M 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28
             L 682 28 L 682 88 L 665 100 L 665 394 L 682 404 L 682 435
             L 670 455 L 5 455 L 5 30 z"
              fill="rgba(19,101,233,.4)" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round" />
右侧小方块和小线段
 <path d="M 673 102 L 678 102 L 678 122 L 673 122 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 135 L 678 135 L 678 155 L 673 155 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 168 L 678 168 L 678 188 L 673 188 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 201 L 678 201 L 678 221 L 673 221 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 234 L 678 234 L 678 254 L 673 254 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 267 L 678 267 L 678 287 L 673 287 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 300 L 678 300 L 678 320 L 673 320 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 333 L 678 333 L 678 353 L 673 353 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 366 L 678 366 L 678 386 L 673 386 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <!--    右侧小线段-->
        <path d="M 680 92 L 680 101" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 123 L 680 134" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 156 L 680 167" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 189 L 680 200" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 222 L 680 233" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 255 L 680 266" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 288 L 680 299" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 321 L 680 332" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 354 L 680 365" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 387 L 680 400" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>

真是考验耐心,和初中的计算功底呢。

光晕动画
这个是比较难的点,需要好好说说。
  • 假设先画一个比较粗的,围绕着外框的光带。
  • 一个圆在光带中运动,而我们只能看到圆中的部分。
  • 我们看到的部分只有渐变的,咋办呢,那就在mask中加入径向渐变中间实心,外侧透明,这样就有光晕的效果啦。
  <defs>
<!--            第一个光晕的路径-->
            <path id="ec-border-box-path-1" d="M 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28 L 682 28 L 682 88 L 665 100 L 665 394 L 682 404 L 682 435 L 670 455 L 5 455 L 5 30 L 30 5" fill="transparent"></path>
<!--            第二个光晕的路径-->
            <path id="ec-border-box-path-1-c1" d="M 665 394 L 682 404 L 682 435 L 670 455 L 5 455 L 5 30 L 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28 L 682 28 L 682 88 L 665 100 Z" fill="transparent"></path>
<!--            定义径向渐变-->
            <radialGradient id="ec-border-box-r-1" cx="50%" cy="50%" r="50%">
                <stop offset="0%" stop-color="#fff" stop-opacity="1"/>
                <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
            </radialGradient>
<!--            定义遮罩-->
            <mask id="ec-border-box-mask-1">
<!--                定义圆,fill使用了径向渐变-->
                <circle cx="0" cy="0" r="50" fill="url(#ec-border-box-r-1)">
<!--                    定义运动路径-->
                    <animateMotion dur="5s" rotate="auto" repeatCount="indefinite">
                        <mpath xlink:href="#ec-border-box-path-1"></mpath>
                    </animateMotion>
                </circle>
                <circle cx="0" cy="0" r="50" fill="url(#ec-border-box-r-1)">
                    <animateMotion dur="5s" rotate="auto" repeatCount="indefinite">
                        <mpath xlink:href="#ec-border-box-path-1-c1"></mpath>
                    </animateMotion>
                </circle>
            </mask>
        </defs>
<use stroke-width="5" xlink:href="#ec-border-box-path-1" mask="url(#ec-border-box-mask-1)" stroke="rgb(19,101,233)"></use>

总的代码

 <style>
        body{
            background-color:rgb(18,19,37)
        }
        .ec-box{
            position:relative;
            width: 500px;
            height: 400px;
        }
        .border-box-content{
            height: 100%;
        }
        .ec-svg-container{
            position:absolute;
            width: 100%;
            height: 100%;
            left: 0;
            top: 0;
        }
    </style>
<div class="ec-box">
    <svg
            class="ec-svg-container"
            width="697"
            height="462"
            viewBox="0 0 697 462"
            xmlns="http://www.w3.org/2000/svg"
    >
        <defs>
            <path id="ec-border-box-path-1" d="M 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28 L 682 28 L 682 88 L 665 100 L 665 394 L 682 404 L 682 435 L 670 455 L 5 455 L 5 30 L 30 5" fill="transparent"></path>
            <path id="ec-border-box-path-1-c1" d="M 665 394 L 682 404 L 682 435 L 670 455 L 5 455 L 5 30 L 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28 L 682 28 L 682 88 L 665 100 Z" fill="transparent"></path>
            <radialGradient id="ec-border-box-r-1" cx="50%" cy="50%" r="50%">
                <stop offset="0%" stop-color="#fff" stop-opacity="1"/>
                <stop offset="100%" stop-color="#fff" stop-opacity="0"/>
            </radialGradient>
            <mask id="ec-border-box-mask-1">
                <circle cx="0" cy="0" r="50" fill="url(#ec-border-box-r-1)">
                    <animateMotion dur="5s" rotate="auto" repeatCount="indefinite">
                        <mpath xlink:href="#ec-border-box-path-1"></mpath>
                    </animateMotion>
                </circle>
                <circle cx="0" cy="0" r="50" fill="url(#ec-border-box-r-1)">
                    <animateMotion dur="5s" rotate="auto" repeatCount="indefinite">
                        <mpath xlink:href="#ec-border-box-path-1-c1"></mpath>
                    </animateMotion>
                </circle>
            </mask>
        </defs>
        <use stroke-width="5" xlink:href="#ec-border-box-path-1" mask="url(#ec-border-box-mask-1)" stroke="rgb(19,101,233)"></use>
        <!--    三角-->
        <path d="M 5 5 L 20 5 L 5 20 z"
              fill="transparent" stroke-linejoin="round" stroke="rgba(19,101,233)" stroke-width="2" />
        <!--            外框-->
        <path d="M 30 5 L 323 5 L 351 17 L 548 17 L 554 5 L 641 5 L 662 28
             L 682 28 L 682 88 L 665 100 L 665 394 L 682 404 L 682 435
             L 670 455 L 5 455 L 5 30 z"
              fill="rgba(19,101,233,.4)" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round" />
        <!--            上方的线-->
        <path d="M 352 6 L 540 6" stroke="rgb(14,80,191)" stroke-width="3" stroke-linejoin="round"/>
        <!--    右侧小方块-->
        <path d="M 673 102 L 678 102 L 678 122 L 673 122 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 135 L 678 135 L 678 155 L 673 155 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 168 L 678 168 L 678 188 L 673 188 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 201 L 678 201 L 678 221 L 673 221 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 234 L 678 234 L 678 254 L 673 254 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 267 L 678 267 L 678 287 L 673 287 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 300 L 678 300 L 678 320 L 673 320 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 333 L 678 333 L 678 353 L 673 353 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 673 366 L 678 366 L 678 386 L 673 386 z" fill="transparent" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <!--    右侧小线段-->
        <path d="M 680 92 L 680 101" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 123 L 680 134" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 156 L 680 167" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 189 L 680 200" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 222 L 680 233" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 255 L 680 266" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 288 L 680 299" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 321 L 680 332" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 354 L 680 365" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
        <path d="M 680 387 L 680 400" stroke="rgb(14,80,191)" stroke-width="2" stroke-linejoin="round"/>
    </svg>
</div>

最终效果

Rec 0004 - 数据大屏边框(1)
由于是截的gif,比较不流畅。

分类
CSS

CSS复现让你惊叹的PPT技巧(1)

概览

  • 字体中的风景
  • 图像进度条

字体中的风景

需要一个背景,并将背景剪裁到字体。最后将字体的颜色设置为透明。

技术要点
diu - CSS复现让你惊叹的PPT技巧(1)
选用的背景图片

选用的字-MMJJ

合成效果图

Rec 0002 - CSS复现让你惊叹的PPT技巧(1)
由于文章内不好写代码,只能录制为gif。

代码

html

<div class="txt-clip">MMJJ</div>

css

 .txt-clip {
            height: 100vh;
            font-size: 20vw;
            display: flex;
            /*水平居中*/
            justify-content:center;
            /*垂直居中*/
            align-items:center;
            /*背景图*/
            background-image: url(./img/diu.gif);
            background-size: cover;
            /*背景位置*/
            background-position:center 121px;
            /*背景裁剪*/
            background-clip: text;
            -webkit-background-clip: text;
            /*字体颜色设置*/
            color: transparent;
        }

图像进度条

需要一张图片,在两块地方使用,一张原图做背景,一张置灰做内部图片,并重合。经过裁剪显示进度。

上方是思路

选择图片

fenda - CSS复现让你惊叹的PPT技巧(1)

实现效果

Rec 0003 - CSS复现让你惊叹的PPT技巧(1)
由于变成了gif,会有点卡。

代码

html

<div class="img-clip-wrap">
    <img class="img-clip" src="./img/fenda.png" height="300">
</div>

css

     @keyframes progress{
            from {
                /*clip-path:polygon点的集合连接成一个图形,该图形是保留的部分*/
                clip-path:polygon(0 0, 100% 0, 100% 50%, 0 50%);
            }
            to {
                clip-path:polygon(0 0, 100% 0, 100% 0, 0 0)
            }
        }
  .img-clip-wrap{
            height:300px;
            display: flex;
            justify-content:center;
            align-items:center;
            background-image: url(./img/fenda.png);
            background-size:auto 100%;
            background-repeat:no-repeat;
            background-position:center;
        }
.img-clip{
            /*使用滤镜变灰色*/
            filter:grayscale(1);
            /*初始裁剪是裁剪了图片的上半部分*/
            clip-path:polygon(0 0, 100% 0, 100% 50%, 0 50%);
            animation: progress 3s ease-in-out infinite;
        }

总结

  • 在字体中的风景部分,主要用到了背景设置,背景裁剪技术。同样,也可以用mask遮罩实现该效果,这里暂且不表。
  • 在图像进度条部分,主要用到了filter滤镜,clip-path裁剪。
分类
Promise

实现一个批量请求函数 multiRequest(urls, maxNum)

描述

要求最大并发数 maxNum,请求完成后返回结果。

代码

  function loadImg(url) {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                const k = Math.random() * 10;
                if (k > 0.1) {
                    resolve(`${url}${k}`);
                } else {
                    reject('加载失败')
                }
            },20)
        })
    }
function multiRequest(urls, maxNum) {
        // 被分割的urls
        const gUrls = [];
        // 当前被分割的urls的下标
        let index = 0;
        // 若成功,返回的结果
        const result = [];
        // url分割
        for (let i = 0; i < urls.length; i+=maxNum) {
            gUrls.push(urls.slice(i, i+maxNum))
        }
        // 这是个Promise
        return new Promise((resolve,reject) => {
            // 执行函数
            const request = (resolve, reject) => {
                if (index < gUrls.length) {
                    const plist = [];
                    gUrls[index].forEach(item => {
                        plist.push(loadImg(item));
                    })
                    // 请求到之后
                    Promise.all(plist).then((res) => {
                        res.forEach(item => {
                            result.push(item);
                        })
                        index++;
                        // 放在Promise的then里,请求完这一次再执行
                        request(resolve, reject);
                    }).catch(e => {
                        // 失败就reject
                        reject(e);
                    });
                } else {
                    // 所有执行完成
                    resolve(result);
                }
            }
            // 开始执行
            request(resolve, reject);
        })
    }
    const urls=['1','2','3','1','2','3','1','2','3','1','2','3','1','2','3','1','2','3','1','2','3'];
    multiRequest(urls,4).then(res => {
        console.log(res);
    }).catch(err => {
        console.log(err)
    });

要点

  • 请求urls的分割。
  • 请求完这一波,再请求下一波。
  • 合并Promise。
  • Promise.all进行请求。
分类
Promise

实现Promise.retry

描述

实现 Promise.retry,成功后 resolve 结果,失败后重试,尝试超过一定次数才真正的 reject

代码

    // 定义
    Promise.retry = function(fn, num) {
        // 最终是返回一个Promise
        return new Promise((resolve, reject) => {
            // 执行函数
            let action = function (resolve, reject) {
                fn().then((prod) => {
                    resolve(prod);
                }).catch(e => {
                    if (num > 0) {
                        // 不为0递归调用执行函数
                        action(resolve,reject);
                    } else {
                        // 数量为0则拒绝
                        reject(e);
                    }
                    num--;
                })
            }
            // 调用执行函数
            action(resolve,reject);
        })
    }
    // 测试
    Promise.retry(() => {
        let num = Math.random()*10;
        return new Promise((resolve,reject) => {
            if (num > 5) {
                resolve(num);
            } else {
                reject('小于5');
            }
        })
    },5).then((prod) => {
        debugger;
    }).catch((err) => {
        debugger;
    })

要点

  • Promise.retry是有then的,所以返回new Promise。
  • 递归调用执行函数,若成功则返回,失败则继续调用,直至num归0。
分类
为方便开发而开发的组件

动态增减表单项组件

开发背景

在某次的开发中遇到这样一个需求,表单中有一个具体的属性,该属性对应一个数组对象。

qi ye wei xin jie tu 15939419538073 - 动态增减表单项组件
在ant-design-react中看到了比较好的实现。

但是,项目使用的框架是ant-design-vue。该框架的实现方法是定义一个keys,当keys为3时相当于数组的长度为3。提交时获取的值是firstName1,firstName2,firstName3, lastName1, lastName2,lastName3。这就相当不友好了,每次向后台传值都要进行一番转义。如果需求就这一个那是可以接受的。后来这样的需求变成了9个…。

代码

首先要解决的问题当然是与ant-design-vue的form表单进行联动,可以通过ant-design-vue的form文档。了解如何自定义表单控件。为了在下面解释这个组件的内部逻辑,先把代码贴出来。

<template>
  <div :class="[drag?'drag-wrap':'']">
    <div :class="['group-wrap', bordered? 'border-wrap':'']" v-for="key in keys" :key="key">
      <div class="item" v-if="param1&&!getUnVisible(1)">
        <slot name="param1" :index="key - 1" :innerValue="paramsList[key - 1][param1]" :paramName="param1">
          <a-input
            :disabled="disabled"
            :value="paramsList[key - 1][param1]"
            :placeholder="placeholder1"
            @change="handleChange(key - 1, $event, param1)"
          ></a-input>
        </slot>
      </div>
      <div class="item" v-if="param2&&!getUnVisible(2)">
        <slot name="param2" :index="key - 1" :innerValue="paramsList[key - 1][param2]" :paramName="param2">
          <a-input
            class="item"
            :disabled="disabled"
            :placeholder="placeholder2"
            @change="handleChange(key - 1, $event, param2)"
            :value="paramsList[key - 1][param2]"
          ></a-input>
        </slot>
      </div>
      <div class="item" v-if="param3&&!getUnVisible(3)">
        <slot name="param3" :index="key - 1" :innerValue="paramsList[key - 1][param3]" :paramName="param3">
          <a-input
            :disabled="disabled"
            class="item"
            :placeholder="placeholder3"
            @change="handleChange(key - 1, $event, param3)"
            :value="paramsList[key - 1][param3]"
          ></a-input>
        </slot>
      </div>
      <div class="item" v-if="param4&&!getUnVisible(4)">
        <slot name="param4" :index="key - 1" :innerValue="paramsList[key - 1][param4]" :paramName="param4">
          <a-input
            :disabled="disabled"
            class="item"
            :placeholder="placeholder4"
            @change="handleChange(key - 1, $event, param4)"
            :value="paramsList[key - 1][param4]"
          ></a-input>
        </slot>
      </div>
      <div class="item" v-if="param5&&!getUnVisible(5)">
        <slot name="param5" :index="key - 1" :innerValue="paramsList[key - 1][param5]" :paramName="param5">
          <a-input
            :disabled="disabled"
            class="item"
            :placeholder="placeholder5"
            @change="handleChange(key - 1, $event, param5)"
            :value="paramsList[key - 1][param5]"
          ></a-input>
        </slot>
      </div>
      <!--      <div v-if="param3" class="gap-line">&#45;&#45;</div>-->

      <div v-if="!aline" class="remove-item" @click="onRemoveItem(key - 1)"><icon-font type="icontubiao_shanchu"/></div>
      <div v-if="!aline&&drag" class="drag-thumb" draggable="true"><icon-font type="icontubiao_tuozhuai"/></div>
      <div class="extra-line" v-if="dpsparam"><slot name="dpsparam" :index="key-1" :innerValue="paramsList[key -1][dpsparam]" :paramName="dpsparam"></slot></div>
    </div>
    <a-button type="dashed" v-if="!aline" block @click="onCreateItem">
      <a-icon type="plus" /> <slot name="btnName">添加</slot>
    </a-button>
  </div>
</template>
<script>
let target;
export default {
  props: [
    'value',
    'only', // 是否只显示一行,且没有添加
    'drag', // 是否支持拖拽排序
    'param1',
    'param2',
    'param3',
    'param4',
    'param5',
    'dpsparam',
    'disabled',
    'bordered',
    'placeholder1',
    'placeholder2',
    'placeholder3',
    'placeholder4',
    'placeholder5',
    'UnVisibleArr', // 动态不显示[1,2,3,4,5]
  ],
  data() {
    const value = this.value || [];
    return {
      keys: value.length,
      paramsList: value || [],
      // 是否只显示一行
      aline: this.only !== undefined,
      // param1: "attributeName",
      // param2: "attributeValue"
    };
  },
  name: 'ParamsInput',
  computed: {
    getUnVisible() {
      return (i) => {
        if (this.UnVisibleArr) {
          return this.UnVisibleArr.includes(i);
        }
        return false;
      };
    },
  },
  watch: {
    value(val = []) {
      this.paramsList = val || [];
      this.keys = this.paramsList.length;
    },
    disabled(val = false) {
      if (Object.prototype.toString.call(val).includes('Boolean')) {
        this.disabled = val;
      } else {
        this.disabled = false;
      }
    },
  },
  mounted() {
    if (this.drag) {
      this.onDragMethod();
    }
  },
  methods: {
    onDragMethod() {
      const dropbox = [].slice.call(document.querySelectorAll('.drag-wrap'));
      dropbox.forEach((item) => {
        item.addEventListener('dragstart', (event) => {
          target = event.target.parentNode;
        });
        item.addEventListener('dragover', (event) => {
          event.preventDefault();
        });
        item.addEventListener('drop', (event) => {
          event.stopPropagation();
          event.preventDefault();
          let currentTarget;
          [].slice.call(event.currentTarget.querySelectorAll('.group-wrap')).forEach((res) => {
            const rectRange = res.getBoundingClientRect();
            if (event.y >= rectRange.y && event.y <= rectRange.y + rectRange.height) {
              currentTarget = res;
            }
          });
          if (currentTarget) {
            const rect = currentTarget.getBoundingClientRect();
            const y = rect.y + rect.height / 2;
            switch (target.compareDocumentPosition(currentTarget)) {
              case 2:
                if (y > event.y) {
                  currentTarget.before(target);
                } else {
                  currentTarget.after(target);
                }
                break;
              case 4:
                if (y > event.y) {
                  currentTarget.before(target);
                } else {
                  currentTarget.after(target);
                }
                break;
              default:
                break;
            }
          }
        });
      });
    },
    onCreateItem() {
      this.keys = this.keys + 1;
      const obj = {};
      this.paramsList.push(obj);
    },
    onRemoveItem(k) {
      const paramsList = [...this.paramsList];
      paramsList.splice(k, 1);
      this.triggerChange(paramsList);
      this.keys = this.keys - 1;
    },
    handleChange(key, event, paramName) {
      const paramsList = [...this.paramsList];
      paramsList[key][paramName] = event.target.value;
      this.triggerChange(paramsList);
    },
    // 外部使用
    handleValueChange(key, value, paramName) {
      const paramsList = [...this.paramsList];
      paramsList[key][paramName] = value;
      this.triggerChange(paramsList);
    },
    // 外部使用-列改变
    handleColumnValueChange(key, value, paramName, otherValue = undefined) {
      const paramsList = [...this.paramsList];
      paramsList.forEach((item) => {
        item[paramName] = otherValue;
      });
      paramsList[key][paramName] = value.target.checked;
      this.triggerChange(paramsList);
    },
    triggerChange(changedValue) {
      this.$emit('change', changedValue);
    },
  },
};
</script>
<style scoped>
.group-wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.item {
  flex: 1;
}
.item + .item {
  margin-left: 45px;
}
.gap-line {
  flex: none;
  width: 50px;
  display: flex;
  justify-content: center;
  align-items: center;
}
.item + .item::before {
  content: "--";
  position: absolute;
  width: 40px;
  font-size: 14px;
  margin-left: -28px;
}
.remove-item {
  flex: none;
  width: 35px;
  text-align: center;
  cursor: pointer;
}
.drag-thumb{
  flex: none;
  width: 35px;
  text-align: center;
  cursor: pointer;
}
  .extra-line{
    width: 100%;
  }
  .border-wrap{
    border-width: 1px;
    border-style: solid;
    border-radius: 5px;
    border-color: #e8e8e8;
    padding: 10px;
    margin: 0 0 16px;
  }
</style>

组件功能

参数说明类型默认值
btnName按钮名称,在组件内加入添加配送方式template‘添加’
dpsparam自定义组件内加入一个自定义组件template
unVisibleArr动态不显示的组件,用于被关联时某列不显示。Array[]
only显示一行内容,隐藏添加按钮Booleanfalse
param<index>index范围是1-5,例如param1=”attributeList”,若需自定义组件,则在组件内内容。index为当前行,innerValue为当前值,paramName为该列属性名。String
placeholder<index>index范围是1-5,例如placeholder1=”属性名”,定义了param若需要加上placeholder则添加,若param运用了自定义组件,则不需要。String
disabled是否不可输入,若定义了自定义组件,则不需要。Booleanfalse
bordered加入边框Booleanfalse
drag是否支持拖拽排序Booleanfalse
功能点1:作为一个form控件
  • props接收value参数,通过value知道数组列表的长度keys,数组列表的内容paramsList
  • 为input组件绑定值,我们可知该input为第几行的数据,并知道是哪个字段,因此可以通过paramsList[第几行][哪个字段]来绑定。
  • 给input设置change,每个input的change改变都会影响整体的值,因此我们会使用change函数来改变整体的值。input使用的change函数是handleChange,通过它改变整体的值,并且调用了triggerChange,通知包含该组件的form知道里面有值改变了。
功能点2:需要能够自定义子组件,例如选择等
  • 通过slot作用域插槽,将index(是第几行),paramName(该列的名称),innerValue(该列的值)传递到外部。
  • 外部使用时在该组件上定义ref例如ref=”list”,方便调用该组件内部的函数
  • 外部使用该组件可使用的插槽名为param1,param2,…,param5。
<template v-slot:param1={ index, innerValue, paramName}>
<a-select
  @change="refChange(index, $event, paramName)"
  placeholder="选择承运商"
  :value="form.getFieldValue('baseWhCarriers')[index][paramName]"
>
  <a-select-option v-for="(item ,index) in  autoDict.CARRIER" :value="item.id.toString()" :key="index">{{item.carrierName}}</a-select-option>
</a-select>
</template>
  • 通过ref使用改变组件内的值的函数,函数有两个可供调用handleValueChange(key,value,paramName),该函数接收当前在第几行key,变更的值value,该列名称paramName,用于改变某行某列的值。还有handleColumnValueChange(key,value,paramName,otherValue=undefined),该函数接收的值与上面的函数类似,多了一个otherValue,用于改变某行某列的值,并将该列的其他值都变为otherValue。
  • 绑定value

功能点3:动态增减表单组件递归

qi ye wei xin jie tu 15940035951799 - 动态增减表单项组件
<a-form-item
            :colon="false"
            label="配送方式"
            class="span2"
            v-bind="modalLayout"
          >
            <params-input
              ref="list"
              param1="methodName"
              param2="distMethod"
              dpsparam="baseCarrierDists"
              placeholder1="名称"
              placeholder2="承运商code-请输入编码"
              :bordered="true"
              v-decorator="['otherWays',{
                initialValue: [{}],
                rules: [{
                  required: true,
                  message: '请填写配送方式',
                }]
              }]"
            >
              <template v-slot:btnName>
                添加配送方式
              </template>
              <template v-slot:dpsparam="{ index, innerValue, paramName}">
                <params-input
                  :value="dockForm.getFieldValue('otherWays')[index][paramName]"
                  param1="paramCode"
                  param2="paramName"
                  param3="paramValue"
                  placeholder1="参数名"
                  placeholder2="参数code"
                  placeholder3="参数值"
                  @change="refChange(index, $event, paramName)"
                >
                  <template v-slot:btnName>
                    添加参数
                  </template>
                </params-input>
              </template>
            </params-input>
          </a-form-item>
  • 在组件中调动该组件,使用的插槽是dpsParam,实现逻辑与自定义子组件并无多大区别,只是对dpsParam插槽的位置做了ui处理。
  • 加上:bordered=”true”用于区分内容块
  • 使用插槽btnName修改添加一行的名字

如何使用npm引入

https://github.com/zhuishao/ec-zs-components中的README有引入说明

使用文档

文档