前端
2021年的那些亮点代码



推荐:⭐⭐⭐⭐

new class {add(){return this}} 来链式调用add
checkSecret (target) {
    class Queue {
        constructor (target) {
            this.target = target
            this.result = true
        }
        add (str) {
            if(this.result === true && this.isUndef(str)) {
                this.result = false
            }
            return this
        }
        isUndef (str) {
            return this.target[str] == ''
        }
    }
    return new Queue(target)
},
...
const condition = this.checkSecret(subSecret).add('keyId').add('keySecret')
if (...) {
    condition.add('appId')
} else if (...) {
    condition.add('appId').add('loginName').add('loginCode')
}
...
if (condition.result) {
    ...
} else {
    this.$message({
        type: 'warning',
        message: '请输入完整再提交,内容不能包括星号!'
    })
}
  • 达到【根据不同条件判断不同字段均不为空的效果】
v-on="$listener" v-bind="$attr"实现爷孙透传效果
// CommonTable: 公共表格
<el-table :data="data" v-bind="$attrs" v-on="$listeners">
    <slot name="top"></slot>
...
</el-table>
props: {
    align: {
        type: String,
        default: 'left'
    },
    data: {
        type: Array,
        required: true
    },
    columns: {
        type: Array,
        default: () => []
    }
},
// 使用
 <common-table
        border
        align="center"
        style="width: 600px"
        :data="positionInfo.list"
        :columns="getColumns(positionInfo.name)"
        @clearSelection="clearSelection"
/> 

推荐:⭐⭐⭐

~~num: 可向下取整,常用于读后端0|1的配置项(带str,null,undefined都不报错,=0)
// 收费标识:0|免费 1|收费
this.chargeFlag = ~~data.chargeFlag
// ~~'1' === ~~1.1 === ~~1.7 === 1
// ~~'0' === ~~undefined === ~~null === ~~'ff' === 0
json声明+|functinal|slots|jsx|的组合
  • 实现table渲染,灵活且可选择多,比json的table,自由度更高

<template slot-scope="scoped">
    <template v-if="$scopedSlots[column.scopedSlots]">
        <slot v-bind="{ ...scoped }" :name="column.scopedSlots" />
    </template>
    <template v-else-if="column.customRender">
        {{ column.customRender(scoped.row) }}
    </template>
    <template v-else-if="column.render">
        <free-render :scope="scoped" :render="column.render" />
    </template>
    <template v-else>
        {{ scoped.row[column.prop] }}
    </template>
</template>
// 使用示例
<common-table ... :columns="columns">
    <div slot="userIdSlots" slot-scope="scope">
        <el-tag>{{scope.row.userId}}</el-tag>
    </div>
</common-table>
columns = [
    {
        prop: 'onsiterPhone', // el-table的常规(读prop)模式
        label: '手机号',
    },
    {
        scopedSlots: 'userIdSlots' // slot模式渲染特殊组件
    },
    {
        prop: 'operationTm',
        label: '操作时间',
        width: 160,
        customRender: ({ operationTm }) => { // functinal模式渲染字典值
            return this.$options.filters.formDate(operationTm)
        },
    },
    {
        prop: 'refundAccountName',
        label: '退款账户',
        render: (h, { row }) => { // jsx模式渲染特殊组件
            return (
                <span onClick={() => this.showAccountInfo(row)} class="text-primary cursor">
                    {row.refundAccountName}
                </span>
            )
        }
    },
]
  • 父判断渲染时有无slot,有加载具名slot并传行列信息到slot-scope;有无简单的函数返回str,有用函数渲染;有无jsx,有用jsx render;
// freeRender
functional: true,
props: {
    scope: Object,
    render: Function
},
render: (h, ctx) => {
    return ctx.props.render ? ctx.props.render(h, ctx.props.scope) : ''
}
  • 传jsx类型的render,scope传el-table-column的slot-scope,为json声明传入行列信息
canvas拼凑文字和图片

/**
 * 绘制文字
 * @param  { CanvasRenderingContext2D  } canvasContext canvas 对象
 * @param { string } text 文字
 * @param { number } y y坐标
 * @param { number? } maxWidth 绘制的最大宽度。如果指定了值,并且经过计算字符串的值比最大宽度还要宽,字体为了适应会水平缩放(如果通过水平缩放当前字体,可以进行有效的或者合理可读的处理)或者使用小号的字体。
 */
drawText: (canvasContext, text, y, maxWidth = canvasContext.canvas.width) => {
    const { width } = canvasContext.measureText(text)
    const height = canvasContext.font.match(/(\d+)px/)?.[1] ?? 0
    const distWith = (maxWidth - width) / 2
    const distHeight = y + height / 2
    canvasContext.fillText(text, distWith, distHeight, maxWidth)
},
  • measureText() 方法返回一个关于被测量文本TextMetrics 对象包含的信息(例如它的宽度)
  • 文字在canvas水平居中的实现和css的translateX居中方式类似

推荐:⭐⭐

svg,defs,marker,path,line画图标
<svg width="200" height="20" >
    <defs>
        <marker
            viewBox="0 0 20 20"
            id="arrow-1"
            markerWidth="10"
            markerHeight="10"
            refX="0"
            refY="3"
            orient="auto"
            markerUnits="strokeWidth"
        >
            <path d="M0,0 L0,6 L9,3 z" fill="#FF3C53" />
        </marker>
    </defs>
    <line
        x1="0"
        y1="50%"
        x2="180"
        y2="50%"
        stroke="#FF3C53"
        stroke-width="5"
        marker-end="url(#arrow-1)"
    ></line>
</svg>
递归实现渲染el-table-column逻辑
const h = this.$createElement
const mapElement = (columns) => {
    return columns.map((sub) => {
        let res
        if (sub.children) {
            const which = Object.prototype.toString.apply(sub.children)
            if (which === '[object Function]') {
                res = sub.children()
            }
        }
        // 输出vnode
        return h(
            'el-table-column',
            {
                props: sub,
            },
            res ? mapElement(res) : []
        ) 
    })
}
if (this.$attrs.columns) {
    return mapElement(this.$attrs.columns)
}
columns = [
    {
        prop: 'reviseClockInNum',
        label: '补卡次数',
        align: 'center',
        width: 120
    },
    {
        prop: 'leaveExtend',
        label: '请假',
        align: 'center',
        children:()=>{
            [
                {
                    label: '事假',
                    width: 100,
                    align: 'center',
                },
                {
                    label: '病假',
                    width: 100,
                    align: 'center',
                },
            ]
        }
    }
]
数组裁剪
list.length = 1  // 简单粗暴

推荐:⭐

modalMixin达到让业务组件少写点data,method代码的效果
// mixinModal.js: 聚合弹窗逻辑
export default {
    props: {
        visible: Boolean,
        title: String,
    },
    data () {
        return {
            isShow: false
        }
    },
    methods: {
        closeModal () {
            this.$emit('update:visible', false)
        },
    },
    mounted () {
        this.isShow = true
    },
}
// batch-choice-modal.vue: 业务弹窗组件 
<el-dialog
        @closed="closeModal"
        @close="isShow=false"
        :close-on-click-modal="false"
        :title="title"
        :visible="isShow">
        ...
</el-dialog>
import mixinModal from './mixinModal.js'
export default {
    mixins: [ mixinModal ]
}
// 使用
<batch-choice-modal
    v-if="isShowChoiceModal"
    :visible.sync="isShowChoiceModal"
    title="批量推广(按选中)"
/>
...
this.isShowChoiceModal=true
v-on="$listener" v-bind="$attr"实现爷孙透传效果
// CommonTable: 公共表格
<el-table :data="data" v-bind="$attrs" v-on="$listeners">
    <slot name="top"></slot>
...
</el-table>
props: {
    align: {
        type: String,
        default: 'left'
    },
    data: {
        type: Array,
        required: true
    },
    columns: {
        type: Array,
        default: () => []
    }
},
// 使用
 <common-table
        border
        align="center"
        style="width: 600px"
        :data="positionInfo.list"
        :columns="getColumns(positionInfo.name)"
        @clearSelection="clearSelection"
/> 
import CommonTable from '@c/business/table'
  • 未声明的border组成$attrs;clearSelection组成$listeners 达到透传el-table效果
header,section 语义化标签
<section class="mt20 ycoin-form">
    <header class="tokens-header">充值金额</header>
    ...
</section>
<section class="mt20 ycoin-form">
    <header class="tokens-header">收币人</header>
    ...
</section>

开始时间0点,结束时间23:59:59
export const generatorTime = time => {
    const [startTime, endTime] = time || ['', '']
    return [
        startTime,
        endTime &&
            dayjs(endTime)
                .endOf('day')
                .valueOf()
    ]
}
// 使用
computed: {
    params() {
        const { time, ...rest } = this.form
        const [startTime, endTime] = generatorTime(time)
        return {
            condition: {
                ...rest,
                startTime,
                endTime
            }
        }
    }
}
find结合?.,实现一行寻找array某key的value
const mode = a.find(({ label }) => label == b)?.desc || ''
监听compositionstart和compositionend阻止输入拼音回车前的input事件
const trigger = (el, type) => {
    const e = document.createEvent('HTMLEvents')
    e.initEvent(type, true, true)
    el.dispatchEvent(e)
}
const compositionstart = e => {
    e.target.composing = true
}
const compositionend = e => {
    e.target.composing = false
    trigger(e.target, 'input')
}
const callback = e => {
    if (!e.target.composing) { // 中文键入时,只在回车或者清空的状态才抛input事件
        handler(ele, binding)
        trigger(ele, 'input')
    }
}
ele.addEventListener('keyup', callback)
ele.addEventListener('compositionstart', compositionstart)
ele.addEventListener('compositionend', compositionend)
lodash的分组chunk,实现左右两栏table
import { chunk } from 'lodash'
chunk(
    [
        [{ label: '楼盘名称', value: val.estateName }],
        [{ label: '物业类型', value: val.propertyTypeName },
        [{ label: '纳税人识别号', value: val.taxpayerNo }],
        [{ label: '公司类型', value: val.companyTypeName }],
    ],2
)
->
[
    [{label: '楼盘名称', value: val.estateName}], 
    [{label: '物业类型', value: val.propertyTypeName}],
],
[
    [{label: '纳税人识别号', value: val.taxpayerNo}],  
    [{label: '公司类型', value: val.companyTypeName}] ,
]
利用嵌套校验省掉一层
// 校验必填项
export const validateRequired = (message, trigger = 'blur') => ({
    required: true,
    message,
    trigger,
})
// 校验数组必填项
export const validateArrayRequired = (...rest) => ({
    type: 'array',
    ...validateRequired(...rest),
})
// 使用
const rules = {
    projectManageList: [validateArrayRequired('请添加项目经理', 'change')],
    planningList: [validateArrayRequired('请添加策划专员', 'change')],
    calculationPoint: [validateRequired('请设置佣金计算起点', 'change')],
}
??取代||。只要不是null和undefined就取前者
branchList({ indChannelList }) {
    // return getFilterChannelList(indChannelList.branchList)
    return indChannelList?.branchList ?? []
},
简单的空格隔开4位数,常用在银行卡号展示
val  = '23vnsour.345234ot2wgnbs;fej'
val.replace(/(.{4})/g, '$1  ')
// '23vn  sour  .345  234o  t2wg  nbs;  fej'
demo级别的代码
addressList.sort(({ defaultStatus: a }, { defaultStatus: b }) => b - a)
// 解构成a,b,近似demo的排序
多行数字的正则验证
let reg = /^[0-9]+(\n[0-9]+)$/
if(!reg.test(value)) { 
  callback(new Error('输入信息不合要求,请按要求重新填写'))
}
/*
    '1\n2\n3' √
    '1 \n2\n3' ×
    '1 1\n2\n3' ×
    ' 1' ×
*/

完结。



日期:2022-02-23 15:22 | 阅读:153 | 评论:0