项目问题点汇总

494 阅读3分钟

页面滚动问题

// 页面滚动属性

clientHeight:元素宽高,包含内容+内边距
offsetHeight: 元素高度包含内容+内边距+边框;
elem.offsetTop: 是获取元素elem元素到网页顶部的距离
scrollTop:网页滚出手机顶端之后,网页的顶部,距离手机顶部的距离
// document.documentElement.scrollTop : 设置的是整个网页滚出窗口的距离;也就是滚出的网页顶部距离窗口顶部的距离
// 元素到窗口顶部的距离= 元素到网页顶部的距离-整个网页滚出窗口的距离


// ================分割线(记一次滚动例子)
// 目的:使得ele元素滚动到手机屏幕的红线下方;手机顶部距红线的距离是course-header部分。
// 获取ele到网页的高度
let scrollRange = document.getElementsByClassName('ele')[0].offsetTop
// 获取course-header的高度
let headerHeight = document.getElementsByClassName('course-header')[0].offsetHeight

//document.documentElement.scrollTop 和 document.body.scrollTop基本一样,同时都设置为了兼容性考虑
// 设置网页滚出的网页头部距离手机屏幕的距离(这里需要想明白一点是:ele元素滚动的距离等于网页滚出的头部距离手机顶端的距离)
document.documentElement.scrollTop = scrollRange - headerHeight
document.body.scrollTop = scrollRange - headerHeight


// 1. 滚动网页(可滚动任意位置)
scrollCurrentLyUnavailable() {
    const currentlyUnavailableElem = this.$refs?.currentlyUnavailableRef
    if (typeof currentlyUnavailableElem != 'object') return
    // 下面这种可以慢慢顺滑滚动
    window.scrollTo({
        top: currentlyUnavailableElem?.offsetTop,
        behavior: 'smooth'
    })
},

// 2. 直接操作滚动元素(scrollIntoView的缺陷:只能是 start、center、end,不能任意指定滚动位置)
validate() {
    this.$nextTick(() => {
        this.$refs.email.validate().catch(err => {
            this.scrollToView(this.$refs.email)
            console.error(err)
        })
    })
},
scrollToView(ref) {
    // block的取值只能是 start、center、end,不能任意指定滚动位置
    ref?.$el?.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' })
},

image.png

image.png

// 可以设置滚动条使得页面在y轴上滚动,也就是页面向上滚动500
// 设置之后立即滚动,等价于设置document.documentElement.scrollTop = 500
// 注意设置window.scrollY = 500 ,网页不滚动的。
window.scrollTo(0, 500)


业务场景:select下拉到底后动态加载的问题(理解scrollHeight)

image.png

image.png

image.png

image.png

image.png

document.querySelector(".el-select-dropdown__wrap").addEventListener("scroll", (e) => {
    console.log(111111111, e.target.scrollHeight)
        console.log(222222222, e.target.scrollTop)
    console.log(33333333, e.target.clientHeight)
})

image.png

juejin.cn/post/690125…

sizecreen22.png

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

真机上滚动时产生缝隙的问题

offsetHeight等计算可能会产生这个问题,可以采用getBoundingClientRect的方式计算,就不会有缝隙问题

vue文件模板图片编译跟html图片编译不一样

写在html或者pug中的图片是可以使用'./img/xxx.png'这种格式,但是在.vue文件的template中写的img标签,'./img/xxx.png'这种格式会报错,只能通过import的形式把图片导入,然后在使用导入的图片对象

移动端24px比例换算

一般将iphone6/7/8作为设计稿标准

iphone6/7/8的width:375px(12px) iphoneSE/5的width:320px(10px) 设计稿750px(24px) chrome能展示的最小字体的大小:12px

但是一般的设计稿我们都用2倍像素写,也就是750px,换句话说现在chrome能展示的就是12*2=24px了,也就是在750px下如果字体小于24px就需要等比例缩放。这样在移动端上才能正常展示

一般使用scale进行缩放之后,需要手动使用margin进行微调

px和rem转换

// 示例1
html {
    font-size: calc(100vw / 7.5);
}

解析:一般会给html设置根字体,比如如果按照设计稿750px写的,750/7.5 = 100倍 那么设计稿的750px下,获取的某元素的48px的宽度,那么48px就会映射为0.48rem

如果看到网页是375px,那么需要将0.48/2,最终结果显示0.24rem

calc&vw&vh

vh理解:如果没有设置vh,那么整体外部wrapper会被内容撑高,可能撑到6000多,但是vh就是给定最外层wrapper视口高度为手机屏幕高度100%

calc理解:calc函数接受一个表达式,表达式运算符的两边需要有空格

// 示例1:这种可以使得banner的左右两边各有40px的外边距
.banner {
  position: absolute;
  left: 40px;
  width: calc(100% - 80px);
}

// 示例2:被解析为一个长度 后跟一个加号 再跟一个负百分比。
calc(8px + -50%)

动画问题

一般A盒子有多余的内容时,我们会将A盒子设置overflow:scroll || hidden,并且给A盒子一个高度比如100px,比如内容的高度是500px,这时候盒子内部的内容就可以滚动了,这里如果想做一个动画,慢慢展示里面的内容,可以给外面的盒子设置动画高度从0慢慢展开到内容的高度就可以了。

两个div盒子默认垂直排列时之间会有空隙

解决办法:给两个div盒子的父元素设置line-height: 0px;

两个div盒子被设置成inline-block时,水平方向存在间隙

解决办法:给两个div盒子的父元素设置letter-spacing: -4px;或者直接给父元素设置font-size:0;

其作用是可以控制文字间的水平距离,可以让文字水平方向上重叠(line-height是让文字垂直方向上重叠),这样就抵消了空格或换行的占位。

如何让行内元素宽高有效

比如a标签,设置宽高之后无效,这时候通过下面方式使得a标签的宽高有效

  1. 给a标签设置float
  2. 设置a标签为行内块

Label标签的作用和使用

定义:它通常关联一个控件

Label标签有两个属性,一个是for,一个是 accesskey

for功能:表示这个Lable是为哪个控件服务的,Label标签要绑定了for指定HTML元素的ID或name属性,你点击这个标签的时候,所绑定的元素将获取焦点 ,点击label所包裹内容,自动指向for指定的id或name。(这个可以用在轮播图中,有点类似于a标签点击触发锚点的效果)

accesskey:定义了访问这个控件的热键( 所设置的快捷键不能与浏览器的快捷键冲突,否则将优先激活浏览器的快捷键)

// 示例1:这里点击姓名两个字就相当于点击input输入框一样
<input id="username" type="text"><label for="username">姓名</label>
// 示例1:
<label for="username" accesskey="N">姓名</label><input id="username" type="text">

:target伪类

:target 是css3的伪类目标选择器,URL后面跟锚点#,指向文档内某个具体的元素。这个被链接的元素就是目标元素(target element),:target选择器用于选取当前活动的目标元素。

比如:http://127.0.0.1:8080/lunboDemo1.html#b 那么:target就会匹配到锚点b元素

给标签设置class问题

不要直接给标签加样式,这样有些标签的子元素会继承父元素的某些不需要属性会扰乱布局;建议给标签加class后再加样式

flex采坑点记录

  1. flex布局中flex的直接子元素设置floatclear以及vertical-align属性是无效的。

  2. flex布局中的直接子元素的排列的最后一个子元素的margin-right是无效的;如果空间没有占满的话。可以通过margin-left:auto设置排列的最后一个元素靠父盒子的右边界对齐

  3. flex布局中的非直接子盒子是可以使用float属性的

  4. float属性的盒子中可以使用flex布局

  5. flex布局的盒子可以使用定位属性,但是flex属性对于脱离文档流(绝对定位和固定定位)的盒子是无效的

  6. flex布局为一维布局;flex中的项目会被当做块元素对待,并且是等高。

  7. Ios9以下不支持flex,所以移动端避免使用flex

h5移动端header和跳转问题

可以调用端上的,可以h5自己写,需要跟端上协商好; 如果h5页面中跳转链接那么会直接在当前h5中打开,而不会新开webview;如果想新开webview,可以手动调端上新开一个webview

点击某一个列表项下拉箭头导致全部列表下拉

这里采用在初始化时,给每个item绑定一个变量arrowImgFlag,触发点击事件时候,将item传入,只改变点击的item的arrowImgFlag。这样就只会使得被点击的item下拉

点击tab动态添加下划线

image.png

image.png

切换tab和tab-list数据联动

image.png

::v-deep深度访问

segmentfault.com/a/119000002…

1、>>>

如果vue的style使用的是css,那么则

<style lang="css" scoped>
.a >>> .b { 
   /* ... */ 
}
</style>

但是像scss等预处理器却无法解析>>>,所以我们使用下面的方式.

2、/deep/

<style lang="scss" scoped>
.a{
   /deep/ .b { 
      /* ... */ 
   }
} 
</style>

但是有些开发者反应,在vue-cli3编译时,deep的方式会报错或者警告。
此时我们可以使用第三种方式

3、::v-deep

切记必须是双冒号

<style lang="scss" scoped>
.a{
   ::v-deep .b { 
      /* ... */ 
   }
} 
</style>

4、 补充在vue3中deep的深度访问

:deep(.el-breadcrumb__item .is-link) {
  font-family: PingFangSC-Regular;
  font-size: 14px;
  color: #86909c;
  line-height: 20px;
  font-weight: 400;
}

去除滚动条 & 轮播

  1. flex可以让内部元素等高
  2. 轮播中间层计算宽度
  3. 通过::v-deep修改豆腐块宽度

image.png

image.png

滚动

tab切换时滚动加载,考虑下数据缓存

vuex失效场景

通过router.push的方式跳转vuex有效,因为router跳转的方式是组件局部更新.

以下两种情况失效:

  1. 多页面应用切换页面时vuex失效;
  2. 或者通过location.href跳转页面时vuex失效

vuex失效后的替代方案:

  1. 可以考虑将参数存放在path中,页面跳转事通过route.query获取
  2. 也可以存放在本地storage中。

$router.replace导致页面死循环问题

当跳转页面时,必须采用push方式,而不是replace,两者的区别是:

  1. push:push会在history中添加一条path记录,比如这里的/login,从login后退返回就可以回到当前页面。

  2. replace:replace中指定的path:/login会直接替换当前页面路由,如果当前的页面是顶层路由,如果那么被repalce指定的login页替换之后,从login页就无法回退了;页面卡死循环

image.png

image.png

备注

1. 使用router的query存放参数后,需要this.$forceUpdate()强刷页面

2. 使用router的query存放callback从login返回之后,需要将callback赋值空串

image.png

某些页面下的某些操作需要用户登录才能操作

比如点击提醒按钮邮件通知功能,需要用户登录才可以发送邮件;

两种实现方案:

  1. 可以在当前A页面跳转登录页时,在url中增加callback回调,然后登录成功之后返回到当前A页面,在A页面的mounted中再去判断url中callback,并执行callback回调

image.png

image.png

  1. 除了url回调的方式之外,可以采用在当前A页面弹出模态框,在模态框中登录,这种方式始终在A页面,所以在addNotify中写连贯写弹出模态框登录成功之后做什么,页面不会重新加载。

备注踩坑点是用户有可能去了登录页但是没有登录,又返回到A页面,所以对登录回来后还是需要增加login是否登录的判断.

url编码

跳转链接时最好将url进行编码(前提是跳转后的页面有对url进行解码)

文本溢出截断省略

1. 单行文本溢出省略

  • overflow: hidden;(文字长度超出限定宽度,则隐藏超出的内容)
  • white-space: nowrap;(设置文字在一行显示,不能换行)
  • text-overflow: ellipsis;(规定当文本溢出时,显示省略符号来代表被修剪的文本)

<style>
    .demo {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
</style>
<body>
    <div class="demo">这是一段很长的文本</div>
</body>
// 备注: white-space: nowrap;会戳透当前布局范围,所以需要给父元素设置宽度控制,但是某些时候写固定宽度不利于盒子自适应,所以借助下面的方式,指定
-webkit-line-clamp: 1;为一行显示就可以。

----

display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-transform: capitalize;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;

2. 多行文本溢出省略

  • -webkit-line-clamp: 2(用来限制在一个块元素显示的文本的行数,2表示最多显示2行。 为了实现该效果,它需要组合其他的WebKit属性)
  • display: -webkit-box(和1结合使用,将对象作为弹性伸缩盒子模型显示 )
  • -webkit-box-orient: vertical(和1结合使用 ,设置或检索伸缩盒对象的子元素的排列方式 )
  • overflow: hidden(文本溢出限定的宽度就隐藏内容)
  • text-overflow: ellipsis(多行文本的情况下,用省略号“…”隐藏溢出范围的文本)
  • word-break: break-all (多行文本如果文本是纯数字,就需要加这行,否则会出问题)

image.png

按钮之类的点击加上stop阻止事件冒泡

@click.stop="addCart"

overflow

juejin.cn/post/699285…

box-size

juejin.cn/post/699287…

outline

image.png

img设置图片和backgroun设置图片

img设置图片时,只需要给宽,高度会等比例适应,因为有图片本身就有默认的宽高

同理img也可以只设置高度,宽度也会自适应


background借助url指定的图片为背景图片,必须手动设置盒子的宽高,否则图片不展示。也可能需要设置是否平铺,等比例缩放等。

页面尽量不使用绝对定位,采用flex代替

vue变量提升性能

  1. 定义常量(缺点:模板中不能访问)
  2. 直接在this上挂载
  3. 自定义options
<template> 
    <div>{{ $options.myOptions.test }}</div>
</template>

export default { 
    data() { }, // 自定义options和data同级 
    // 取值时: this.$options.myOptions.test 
    myOptions: { test: '111' }
}

juejin.cn/post/685512…

使用背景样式填充时不生效问题?

// background-size属性不生效
background-size: 100% 100%;
background: url('https://xxx.png';) no-repeat;  //这一句会覆盖之前的其它background属性

解决:使用background简写属性,如果顺序在其他background-size属性之后,会覆盖之前的;所以可以将background: url('https://xxx.png';) no-repeat;写在其他background属性之前;也可以不使用background: url('https://xxx.png';) no-repeat;导入图片,直接使用background-image,则不需要考虑顺序问题。

background: url('https://xxx.png';) no-repeat;
background-size: 100% 100%;

移动端复制内容到剪贴板

copyNum() {
    let oInput = document.createElement('input')
    // TODO:
    oInput.value = 'hello'
    document.body.appendChild(oInput)
    oInput.select() // 选择对象
    document.execCommand('Copy') // 执行浏览器复制命令
    document.body.removeChild(oInput)
}


///////////////////

/**
 * 函数功能:复制内容到剪贴板
 * @param {*} value : 需要复制的内容
 * @returns promise
 */
const copyToClipboard = value => {
    return new Promise((resolve, reject) => {
        let tempInput = document.createElement('input')
        tempInput.value = value
        document.body.appendChild(tempInput)
        tempInput.select()
        document.execCommand('Copy')
        document.body.removeChild(tempInput)
        resolve()
    })
}

copyToClipboard(this.couponInfo?.coupon_alias)
.then(() => this.$toast.success(this.$t('newUserModal.copySuccess')))
.catch(error => this.$toast.fail('Failed!'))

css实现按钮禁用

.disabled {
pointer-events: none;
}

+

@click.stop阻止冒泡

nuxtJSX使用

xie.infoq.cn/article/6af…

备注:this.$emit('updateOrderDetail')发射时,updateOrderDetail首字母是小写,但在render中监听时加on并且updateOrderDetail的首字母大写。参数也可以带过去。

image.png

查找某个属性最终的样式来源

image.png

p元素中有行内元素时,行内元素默认没有从左上角显示

www.jianshu.com/p/48f9805f1…

解决办法:间隙可以使用font-size=0去除间隙(具体可以查看fontSize:0妙用)

行内有时候没有从左上角显示,因为默认的基线问题,行内的基线和p元素的基线。此时设置fontsize=0

input内容居中和光标大小

www.cnblogs.com/mofish/arch…

光标:通过fontSize调整光标大小 内容居中:通过设置line-height === input的height使得内容垂直居中显示

动画设置height:auot不生效

可借助 max-height 的特性来做到动态高度的伸缩。因为transition动画仅支持部分css属性(备注:针对width/height的auto属性不支持)

具体解析见:www.cnblogs.com/coco1s/p/14…

XXX

image.png

空心三角形

www.cnblogs.com/echolun/p/1…

两个三角形叠在一起,借助transparent

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      .box {
        border: 1px solid pink;
        position: relative;
        width: 200px;
      }
      .one {
        border: 100px solid transparent;
        box-sizing: border-box;
        border-bottom: 100px solid #d6d6d6;
      }
      .two {
        position: absolute;
        top: 0px;
        border: 80px solid transparent;
        box-sizing: border-box;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="one"></div>
      <div class="two"></div>
    </div>
  </body>
</html>

字体会戳出去

字体会戳出去,是因为默认是以单词分割的,如果是连续的字串,就会戳出去;可以考虑非单词也截断

ESLint 的 no-prototype-builtins 规则

原文出处:juejin.cn/post/684490…

最近在开发中遇到一个 ESLint 规则问题:

obj.hasOwnProperty('key')

ESLint 给出错误提示:

Do not access Object.prototype method 'hasOwnProperty' from target object. eslint(no-prototype-builtins)

当时没想明白这个规则的设计点在哪里。根据其文字提示“不要从目标对象访问 Object 原型方法”,想到一种解决方案——直接找到这个方法,用 call 改变指向调用:

Object.prototype.hasOwnProperty.call(obj, 'key')

背后原理

今天在想到原型链的时候突然意识到为何 ESLint 不允许从目标对象调用 Object 原型方法。

在 JS 中,往往通过改变原型链实现继承。一旦原型链发生改变,原先可以访问到的原型属性、方法便可能无法访问。考虑最极端的情况,若 obj 原先原型链的最顶端是 Object,此时可以通过原型链访问 Object.hasOwnProperty 方法;而若改变后,顶端不再是 Object,那么访问 obj.hasOwnProperty 访问就会得到 undefined。因此,直接从对象访问原型方法,很可能会带来隐藏的 BUG。

input或者textare上边沿会有黑影

// 去除黑影
-webkit-appearance: none;

event.stopImmediatePropagation()

如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。如果在其中一个事件监听器中执行 stopImmediatePropagation() ,那么剩下的事件监听器都不会被调用。


<!DOCTYPE html>
<html>
    <head>
        <style>
            p { height: 30px; width: 150px; background-color: #ccf; }
            div {height: 30px; width: 150px; background-color: #cfc; }
        </style>
    </head>
    <body>
        <div>
            <p>paragraph</p>
        </div>
        <script>
            const p = document.querySelector('p')
            p.addEventListener("click", (event) => {
              alert("我是p元素上被绑定的第一个监听函数");
            }, false);

            p.addEventListener("click", (event) => {
              alert("我是p元素上被绑定的第二个监听函数");
              event.stopImmediatePropagation();
              // 执行stopImmediatePropagation方法,阻止click事件冒泡,并且阻止p元素上绑定的其他click事件的事件监听函数的执行.
            }, false);

            p.addEventListener("click",(event) => {
              alert("我是p元素上被绑定的第三个监听函数");
              // 该监听函数排在上个函数后面,该函数不会被执行
            }, false);

            document.querySelector("div").addEventListener("click", (event) => {
              alert("我是div元素,我是p元素的上层元素");
              // p元素的click事件没有向上冒泡,该函数不会被执行
            }, false);
        </script>
    </body>
</html>

局部loading实现

商品列表的展示,针对操作的某一商品做局部loading;比对新老列表的时候,不能直接将整个列表赋值,因为渲染时会造成整个商品列表塌陷,只能挨个比对商品,然后替换对应操作的商品。

<template>
    <div class="loading-outer">
        <div v-if="isShowLoadingMoudle" class="loading-moudle">
            <span class="loading-icon">
                <i :class="localIcon" />
            </span>
            <span class="loading-txt">{{ loadingTxt }}</span>
        </div>
        <div :class="activeLoadingClass">
            <slot name="component" />
        </div>
    </div>
</template>

<script>
export default {
    props: {
        // 是否loading
        isLoading: {
            type: Boolean,
            default: false
        },
        // loading文案
        loadingTxt: {
            type: String,
            default: 'Loading'
        },
        // loading图标
        loadingIcon: {
            type: String,
            default: 'cart_loading_icon'
        }
    },
    data() {
        return {
            isShowLoadingMoudle: false
        }
    },
    computed: {
        localIcon() {
            return `iconfont icon${this.loadingIcon}`
        },
        activeLoadingClass() {
            return this.isLoading ? 'loading-warpper active-loading-wrapper' : 'loading-warpper'
        }
    },
    watch: {
        isLoading: {
            immediate: true,
            handler(val) {
                this.isShowLoadingMoudle = val
            }
        }
    }
}
</script>

<style lang="scss" scoped>
.loading-outer {
    position: relative;
}
.loading-warpper {
    position: relative;
    background: #ffffff;
}
.active-loading-wrapper {
    background: #ffffff;
    opacity: 0.3;
}

.loading-moudle {
    display: flex;
    flex-direction: column;
    align-items: center;
    position: absolute;
    z-index: 100;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    .loading-icon {
        width: 64px;
        height: 64px;
        display: flex;
        justify-content: center;
        align-items: center;
        animation: rotate-all-time 0.8s infinite linear both;
        -webkit-animation: rotate-all-time 0.8s infinite linear both;
    }
    .loading-txt {
        margin-top: 16px;
        font-size: 28px;
        font-weight: 400;
        color: #222222;
        line-height: 34px;
    }
}
.iconcart_loading_icon {
    font-size: 55px;
    color: #222222;
}

@keyframes rotate-all-time {
    0% {
        transform: rotate(0deg);
        transform-origin: center;
    }
    100% {
        transform: rotate(360deg);
        transform-origin: center;
    }
}

@-webkit-keyframes rotate-all-time {
    0% {
        transform: rotate(0deg);
        transform-origin: center;
    }
    100% {
        transform: rotate(360deg);
        transform-origin: center;
    }
}
</style>

elementPlus的el-select绑定对象记录

需要借助value-key属性

// 示例1:
        <el-form-item label="省份:">
          <el-select
            v-model="currentProvince.data"
            placeholder="请选择"
            value-key="name"
            @change="changeSelectProvince"
          >
            <el-option
              v-for="province of provinceAndCityList"
              :label="province.name"
              :value="province"
              :key="province.id"
            />
          </el-select>
        </el-form-item>


const changeSelectProvince = (value: any) => {
  currentProvince.data = value;
  currentCity.data = {};
};

image.png

判断属性在对象内(in/hasOwnProperty)

有时候我们需要判断对象中存在某个属性值,有可能他们的值是null/''/0/undefined这些不确定的值;所以不能单纯用if判断,if默认转为boolean判断了

in: in方式可以判断对象的自有属性和继承来的属性是否存在

let obj = {
    name: ''
}
'name' in obj  // true

hasOwnProperty:该方法只能判断自有属性是否存在,对于继承属性会返回false。

let obj = {
    name: ''
}
obj.hasOwnProperty('name') // true

判断文本超过3行则显示浮窗(ts)

/**
 * compute difference between offsetHeight and lineHeights which includes many lines
 * --Do not need to pay attention to size of screen
 * @param el DOMElement
 * @param lines lines of text (default: 3 lines)
 * @returns boolean
 */
export const computeEleLineHeightByLines = (
  el: Element,
  lines: number = 3
): boolean => {
  const styleObj: CSSStyleDeclaration = window.getComputedStyle(el);
  const lineHeight: number = Number(styleObj?.lineHeight?.replace(/px$/, ''));
  // --support all size of screen including zoom in and out
  return (
    Math.abs((el as HTMLElement).offsetHeight - lineHeight * lines) < lineHeight
  );
};

image.png

数组对象去重(ts)

/**
 * filter to repeat Object by the second parameter
 * @param sourceArr source array
 * @param key keyword used to filter
 * @returns array including object of no repeat
 */
export const filterRepeatObjectInArrayByKey = <T, K extends keyof T>(
  sourceArr: T[],
  key: K
): T[] => {
  if (!key) return sourceArr;
  if (Array.isArray(sourceArr) && sourceArr?.length === 0) return sourceArr;
  const tempObj: any = {};
  return sourceArr?.reduce((arr: T[], next: T) => {
    !!tempObj[next[key]]
      ? undefined
      : ((tempObj[next[key]] as boolean) = true) && arr.push(next);
    return arr;
  }, []);
};

image.png

arco的UI组件库修改组件样式不生效问题(因为某些脱离文档流的,在局部组件的scoped中通过类型其实是访问不到的,所以需要再增加style不要scoped)

<style lang="less" scoped></style>

<style lang="less">
  .user-duty-popover {
    padding: 0;
    border: none;
    box-shadow: 0 2px 10px 0 rgba(29, 33, 41, 0.12);
    box-shadow: 0 9px 28px 0 rgba(29, 33, 41, 0.04);

    .arco-popover-content {
      width: 360px;
      max-height: 230px;
      margin-top: 0;
      padding: 16px !important;
      color: #86909c;
      font-weight: 400;
      font-size: 14px;
      line-height: 22px;
      letter-spacing: 0;
    }
  }
</style>

hover元素调试问题

比如hover之后有浮窗,此时查看浮窗dom;鼠标放在待查看的dom,可以鼠标右键之后,什么都不用点,然后就可以移动鼠标到控制台。

关于elementUI库中的el-table表格的横向滚动条

每次重置滚动条:下面是通过在浏览器控制台查看到bodyWrapper;也可以在node_moudles中查看

image.png

        <el-table
          ref="partTimeTableRef"
          :data="partTimeJobData"
/el-table>


      this.$nextTick(() => {
        this.$refs.partTimeTableRef.bodyWrapper.scrollLeft = this.$refs?.partTimeTableRef?.bodyWrapper?.scrollLeft && 0
        // this.$refs.partTimeTableRef.$el.querySelector('.el-table__body-wrapper').scrollLeft = this.$refs?.partTimeTableRef?.$el?.querySelector('.el-table__body-wrapper')?.scrollLeft && 0
      })

image.png

参考

display:inline-block;会产生间隙,以及解决办法

Label标签的作用和使用

文本省略截断