HeiYanjing

HeiYanjing 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

生活本就很艰难,且行且珍惜。

个人动态

HeiYanjing 发布了文章 · 3月25日

基于antd ,react Hooks 搜索条件,列表查询逻辑

一个常见的场景,页面顶部一堆搜索条件,条件的末尾是搜索按钮和重置按钮,下面是一个列表。
设计一套没有问题的搜索,重置逻辑。

第一,将这些搜索条件和搜索按钮,重置按钮封装成一个组件,因为重置也是搜索,所以暴露出一个回调即可。SearchCallBack(...searchObject)

因为antd的Table的pagination有几个属性

const [pagination,setPagination]=useState({
    current:1,
    pageSize:10,
    total:0,
    showTotal:(totalNum:Number) => `共${totalNum}条`,
    pageSizeOptions:['10','20','50','100'];
    showSizeChanger:true,
    showQuickJumper:true,
})

页面中也有一个变量,用于存放搜索条件对象
const [searchObj,setSearchObj] =useState({...defaultSearchObj});
触发搜索操作有三种情况
1,页面初始化完成,各种初始化条件均为空,默认查所有结果

useEffect(()=>{
    search();
},[])

2,页码发生变化,或者一页展示条数发生变化

useEffect(()=>{
    search();
},[pagination.current,pagination.pageSize])

3,搜索或者重置按钮触发

const SearchCallBack=(obj:any)=>{
    setSearchObj({...obj});
    if(pagination.current !== 1){
        setpation({...pagination,current:1,pageSize:10});
    }else{
        search(obj);
    }
}

搜索方法接收一个可选参数

  const  search= (obj ?:any)=>{
    // 如果obj为真,取obj参数,不然就取searchObj参数
  }
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 3月25日

React下不规则字符串转span和Input

遇到一个奇葩需求,接口收到一串算法规则,要求把'[]'内的数字内容转成Input,可供用户修改,其余部分转成文本,修改之后再转成字符串传给后台。这个算法没有规则,可能只有文本,可能有多个'[]'。也不知道中括号会出现在字符串的什么位置。
一开始接到这个需求,一脸懵逼。这两天想到一个还算不错的方法来解决这个问题。
举个例子,某个算法字符串格式:####[1]###[2]#[3]##########[4];

 第一步,将字符串转成html元素
 const [htmlElement,setHtmlElement] = useStae<any>(<></>); // 给需要转换的字符串设置初始值
 const rule = ruleSourceStr; // 赋值原生string
 let result = '';  // 结果
 if(rule.indexOf('[' === '0') { // 第一个字符就是[
    const parseFirst = rule.replace('[',`<Input type='numer' value='`); // 替换第一个字符
    const parseRemainder = parseFirst(/\[/g,`</span><Input type='number' value='`); // 替换剩余中括号[
    result = parseRemainder.replace(/\]/g,'/><span>'); // 替换]
 }else{  // 第一个[]出现在字符串中间
    const parseLeft = rule.replace(/\[/g,`</span><Input type='number' value='>`);
    const parseRight = parseLeft.replace(/\]/g,`/><span>`);
    const addLeft = '<span>' + parseRight; // 在字符串头部加上<span>
    result = addLeft+'</span>';
 }
 setHtmlElement(<div id = 'inner-html' dangerouslySetInnerHtml={{__html:result}}/>); // htmlElement更新后放入相应位置

第二步,相关的span和Input就显示在页面中,这时候加入样式调整一下就可以
第三步,确认之后收集元素内容,转成字符串

const getNode = document.getElementById('innder-html')?.childNodes;
let resultRule = '';
getNode?.forEach((item:any)=>{
    if(item.nodeName === 'SPAN'){
        ruleResult += item.outerText;
    }
    if(item.nodeName === 'INPUT'){
        ruleResult += ('['+ item.value +']');
    }
})
console.log(resultRule); //最终结果
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 2020-09-21

React Hooks使用pdfjs-dist

系统页面中有时候会有一些加载pdf资源的功能需求,pdfjs-dist这个插件可以满足要求。
处理步奏:请求->转换->加载
这里使用的版本是 2.2.228
请求有两种,一种可以是Url链接请求。一种是二进制文件。代码上差别不大,业务逻辑处理如下:

const winW = (document.querySelector('.pdf-calssname') as HTMLDivElement).getBoundingClientRect().width;

const loadingFile = PDFJS.getDocument('your url'); // URL
const laodingFile = PDFJS.getDocument({data:atob(blod)}); //二进制文件

//loading....
loadingFile.promise.then((pdf)=>{
    const nums = pdf.numPages;
    setpageNum(new Array(nums).fill(1)); // pageNum variable
    for(let i =1;i<nums;++i){
        pdf.getPage(i).then((page:any)=>{
            const viewport:any = page.getViewport(i);
            const scale:any = (winW / viewport.width).toFixed(2); //url
            cosnt scale:any = (winW /viewport.width *viewport.scale).toFixed(2) // 二进制文件流
            const scaledViewport = page.getViewport({scale:Math.max(scale,1)*2});
            const canvas:any = document.getElementById('the-canvas'+i);
            const context = canvas.getContext('2d');
            canvas.height = Math.ceil(scaleViewport.height);
            canvas.width = Math.ceil(scaleViewport.width);
            const renderContext ={
                canvasContext:context,
                viewport:scaledViewport,
            }
            const render:any = page.render(renderContext);
            render.then((=>{})
        })
    }
},(err)=>{
    console.log(err);
})

部分tsx代码如下:

<div className = 'pdfdetail-innderbox'>
    {
        pageNum.map((v:number,i:number)=>{
            <canvas key={i} id = {`the-canvas${i+1}`} className = 'pdf-content'>
        })
    }
</div>
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 回答了问题 · 2020-09-15

解决在react中怎么阻止滚动事件呢

在你滑动表页的处理业务里面获取滚轮事件,里面对e.target.className.indexOf('calssName')进行过滤筛选

关注 3 回答 2

HeiYanjing 发布了文章 · 2020-09-10

React路由跳转,弹出新窗口,传参

业务中出现了几个场景:有几个公共的页面需要展示固定的业务,需要在新路由中展示;后来需求改动,要求在新窗口中展示业务。

这样带来几个问题:
1,路由跳转
2,新窗口弹出
3,弹出窗口传参用来请求接口数据,如何传递参数

项目使用hooks开发
首先需要注册路由
<Router path={'路径'} exact={true} component={MyComponent}>
这里面的exact属性默认为false,如果为true时,需要和路由相同时才能匹配,有斜杠也是可以匹配。
如果在父路由中加了exact,不能匹配子路由,建议在子路由中加exact

在实现的过程中,因为习惯问题,我把这一行代码放在<Switch>中较为靠后的位置,把自己坑了一把,后来查了下,这件事情和路由的匹配规则有关系。
1,从上到下匹配, 一旦匹配到了, 就不往下匹配了。错误页面的配置, 就是上面都无法匹配到, 就匹配错误页面。
因为开启了严格匹配,为了养成良好的习惯
2. 长路由放在短路由前面,这里是说,路由前半部分相同的情况 `/a/b` 应该放在 `/a` 前面
3. 长路由放在模糊匹配的前面 `/a/b` 放在 `/a/:id

匹配好路由后就可以直接在代码里面实现跳转了。
首先组件里面要引入react-router-dom
要使用props.history.phush()方法还需要用withRouter

import {withRouter} from 'react-router-dom'
...
props.history.push({pathname:'your router',state:{your parameter}})
...
export default withRouter(memo(MyComponent));

跳转路由的参数就从props.history.location.state中获取

如果需要出现新窗口,路由跳转不适用,需要使用window.open()函数跳转新窗口。这里的Url拼接和新窗口参数获取涉及到location,浏览器F12输入location能看到具体结构。

location属性
hash 设置或返回从井号 (#) 开始的 URL(锚)。如果地址里没有“#”,则返回空字符串。
host 设置或返回主机名和当前 URL 的端口号。
hostname 设置或返回当前 URL 的主机名。
href 设置或返回完整的 URL。在浏览器的地址栏上怎么显示它就怎么返回。
pathname 设置或返回当前 URL 的路径部分。
port 设置或返回当前 URL 的端口号,设置或返回当前 URL 的端口号。
protocol 设置或返回当前 URL 的协议,取值为 ‘http:’,’https:’,’file:’ 等等。
search 设置或返回从问号 (?) 开始的 URL(查询部分)。

location方法
assign() 加载新的文档。
reload() 重新加载当前文档,相当于按浏览器上的“刷新”(IE)或“Reload”(Netscape)键。
replace() 用新的文档替换当前文档,相当于按浏览器上的“刷新”(IE)或“Reload”键。

例子如下

window.opne(location.origin+'your router? parameter & other parmeter');
// 新窗口获取传入参数
location.search
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 2020-09-08

样式中隐藏滚动条

项目中有个长页面,首部是一个轮播组件图,因为页面内容比较多,会出现滚动条,此时在轮播组件最右侧出现滚动条就会显得很难看,需要有个隐藏滚动条操作,css中需要几行简单代码搞定。

// chrom ,safari环境
.container::-webkit-scrollbar{
    display:none;
}
// IE10+环境
.container{
    -ms-overflow-style:none;
}
// firefox环境
.container{
    overflow: -moz-scrollbars-none;
}
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 2020-09-03

React锚点滚动的问题

React锚点滚动,传统的a标签写法如下

<a href = '#div'>To div</a>
<div id= 'div'>DIV</div>

这种写法点击a标签就会跳转,并且伴随路由的改变。

这就很坑爹了·····

因为开发使用Hooks,很多方法变得很尴尬。于是,找到一种还不错的方法。

使用场景,页面有一个展示Pdf的页面,可能会有很多页,有一个固定定位置顶按钮,点击之后处理锚点滚动的逻辑。
代码如下

const scrollToAnchor = (anchorname:any) =>{
    if(anchorname){
        const anchorElement = document.getElementById(anchorname);
        if(anchorElement){
            anchorElement.scrollIntoView({behavior:'smooth',block:'start'});
        }
    }
}

代码里面第一行
const anchorElement = document.getElementById(anchorname);
为了找到锚点
第二行anchorElement.scrollIntoView({behavior:'smooth',block:'start'});是为了平滑的移动到锚点元素顶部

scrollIntoView函数有可配置的参数,如果不加,移动给人一种很生硬的感觉。
它接受两种形式的值:布尔值或对象。接受布尔值主要还是为了兼容不支持平滑滚动(老版)的浏览器

参数值如下:

{
  behavior: "auto" | "instant" | "smooth", // 默认 auto
  block: "start" | "center" | "end" | "nearest", // 默认 center
  inline: "start" | "center" | "end" | "nearest", // 默认 nearest
}

根据需求,合理配置参数就可以了。

查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 2020-08-04

React Hooks下使用antd Modal监听键盘事件,图片预览组件

因为项目中使用了antd.业务上有个需求,需要预览图片。想到了antd里面有个modal.稍作修改就只需要处理前后翻页的业务逻辑就行。
1,可以通过前一张,后一张按钮来进行翻页,图片右上角有退出预览按钮
2,键盘的左右按钮可以前后翻页,ESC按钮可以退出预览

代码如下:

interface IpicPreviewProps{
    index:number; // 第几张图片索引值
    pic:any[];  // 图片链接数组,存储图片地址
    modalShow:boolean; // modal显示和隐藏
    setModalShow:(v:boolean) => void; // 回调
}

function PicturePreview(props:IpicPreviewProps) :JSX.Element {
    const {modalShow,index,pic} = props;
    const [current,setCurrent] = useState('');  // 用于存放当前显示图片地址
    const [currentIndex,setCurrentIndex] = useState(0); // 用于存放当前播放图片在数组中索引值
    
    useEffect(()=>{
        document.addEventListener('keyup',upHandle);
        retrurn ()=>{
            document.removeEventListener('keyup',upHandle);
        }
    },[currentIndex]);
    
    useEffect(()=>{
        setCurrent(pic[index]);
        setCurrentIndex(index);
    },[idnex,pic]);
    
    const leftClick = useMemo(()=>
        ()=>{
            if(currentIndex !== 0){
                setCurrentIndex((prev:number) => prev-1);
                sestCurrent(pic[currentIndex-1]);
            }
        },[currentIndex,pic]);
    
    const rightClick = useMemo(()=>
        () =>{
            const length = pic.length;
            if(currentIndex !== length -1){
                setCurrentIndex((prev:number) =>prev +1);
                setCurrent(pic[currentIndex +1]);
            }
        },[currentIndex,pic]);
        
     const upHandle = useMemo(()=>
        (e:any) =>{
            if(e.keyCode === 37){ // left
                leftClick();
            } else if(e.keyCode === 39){ // right
                rightClick();
            } else if(e.keyCode === 27){ // esc
                props.setModalShow(false);
            }
        },[leftClick,rightClick,props.setModalShow]);
      
      return (
        <Modal
            visible = {modalShow}
            title =''
            centered = {true}
            maskClosable = {false}
            footer ={null}
            closable = {false}
            keyboard = {true}
            className = 'pic-preview-container'>
            <div className = 'content'>
                <div className = 'left-btn' onClick={leftClick}><img src = {left}/></div>
                <div className = 'pic-content'>
                    <img src = {current} className = 'main-pic' />
                    <img src = {close} className='close-btn' onClick{()=>{props.setModalShow(false)}}/>
                 </div>
                 <div className = 'right-btn' onClick={rightClick}><img src = {right}/></div>
             </div>
          </Modal>
      )
        
}

export default Memo(PicturePreview);

样式less文件如下

.ant-modal-root{
    .pic-preview-container{
        width:9rem !important;
        padding-bottom: 0 !important;
        .ant-modal-content{
            background:tansparent;
            box-shadow: 0 0 0 rgba(0 ,0 ,0,0);
            .content{
                background-color:transparent;
                display:flex;
                flex-direction:row;
                align-items:center;
                justify-content:center;
                .left-btn{
                    justify-content:center;
                    align-items:center;
                    display:flex;
                    width: 0.4rem;
                    height:0.7rem;
                    background:#000000
                    opacity:.5;
                    margin-right:.2rem;
                    img{
                        display:inline-block;
                        height:.25rem;
                        width:.15rem;
                    }
                }
                .right-btn{
                    margin-left:.2rem;
                    justify-content:center;
                    align-items:center;
                    display:flex;
                    width:.4rem;
                    height:.7rem;
                    background:#000000;
                    opacity:.5;
                    img{
                        display:inline-block;
                        height:.25rem;
                        width:.15rem;
                    }
                }
                .pic-content{
                    position:relative;
                    .main-pic{
                        max-width:11rem;
                        max-height:8rem;
                    }
                    .close-btn{
                        display:inline-block;
                        width:.4rem;
                        height:.4rem;
                        position:absolute;
                        right:-0.2rem;
                        top:-0.2rem;
                    }
                }
            }
        }
    }
}
查看原文

赞 0 收藏 0 评论 0

HeiYanjing 发布了文章 · 2020-06-24

WEB项目中sessionStorage配置

localStoragesessionStorage是web storage的的两种存储方式,存储客户端临时信息的对象。
localStorage前者用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
sessionStorage存储的数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。

这里可以设计一个SessionStorage类封装一下SessionStorage的API,提供增删改查操作,用户在登录成功后做一些Promise请求,获取一些诸如项目信息,用户信息,权限信息以及所需字典信息等等添加到SessionStorage中。

SessionStorage.ts

export class SessionStorage{
    public SetItem(key:string,option :any){
        try{
            const data:string = JSON.Stringfy(option);
        } catch(err){
            Promise.reject(err);
        }
    }
    
    public GetItem(key:string){
        try{
            const data:string | null = window.sessionStorage.getItem(key);
            if(data === null){
                return data;
            }
            return JSON.Stringfy(data);
        } catch(err){
            Promise.reject(err);
        }
    }
    
    public RemoveItem(key:string){
        try{
            window.sessionStorage.removeItem(key);
        } catch(err){
            Promise.reject(err);
        }
    }
    
    public clear(){
        try{
            window.sessionStorage.clear();
        } catch(err){
            Promise.reject(err);
        }
    }
}

export const session = new SessionStorage();

外面方法需要操作SessionStorage时引入session就可以使用了。

业务操作时

const value = sessionStorage.getItem('key');
查看原文

赞 0 收藏 0 评论 2

HeiYanjing 发布了文章 · 2020-06-23

用于展示附件(文档或者图片)的控件

业务系统中会有一些对附件的展示功能,比如文档或者图片之类,要么能下载,要么能预览,二者定有一条途径可以看到附件内容。
文档类多以链接+文件名的形式展示,点击文件名可以下载文件。
图片也可以以这种方式展示,也可以进行预览,这种方式会更加直观。

控件会需要一个左右翻页效果的两个按钮,当图片数量超过4时可以左右翻页,下载类、预览类分开处理。

AttachComponent.tsx

interface IProps{
    attachData:any;
}
function AttachComponent(props:IProps):JSX.Element{
    const {attachData} = props;
    cosnt [imgList,setImgList] = useState([]);
    const [docList,setDocList] = useState([]);
    const [current,setCurrent] = useState(0); // use variable 'current' set  current img index
    const [moveWidth,setMoveWidth] = useState(0);
    
    useEffect(()=>{
        handleAttachData();
    },[attachData])
    useEffect(()=>{
        handleMove();
    },[current]);
    
    
const handleAttachData =() =>{
    const ImgArr :any = [];
    const DocArr :any = [];
    attachData.forEach((item:any=>{
        cosnt {type} = item; //  distinguish image and doc by type
        if(1 === type){
            ImgArr.push(item);
        }else if(2 === type){
            DocArr.push(item);
        }
    }))
    setImgList(ImgArr);
    setDocList(DocArr);
}

const handleMove =() =>{
    setMoveWidth( current * 100 );
}

// handle page left 
const HandleMoveLeft = () =>{
    setCurrent(current -1 > 0 ? current -1 : 0);
}
// handle page right
const HandleMoveRight = () =>{
    let maxCnt = parseInt((imgList.length / 4).toString());
    if(imgList.length % 4 ){
        maxCnt = maxCnt + 1;
    }
    if(current +1 >=  maxCnt){
        return;
    }
    setCurrent(current + 1 );
}

return(
    <div className = 'attach-container'>
        {docList.length >0 &&
            <ul className = 'doc-list'>
                {
                    docList.map((item:any,index:number)=>{
                        return <li className = 'doc-item' key = {`doc-item-${index}`}><a href ={'doc file url for download'} download ={true}>{item.name}</a></li>
                    })
                }
            </ul>
        }
        {imgList.length > 0 && 
            <div className = 'img-list'>
                <div className = 'img-container'>
                    {
                        imgList.map((item:any,index:number)=>{
                            return <div key={`doc-item-${index}`} className ='img-item'><img src = {'img file url for download'} alt='load file failed'/></div>
                        })
                    }
                </div>
                // when length >4  show page left btn
                {   
                    imgList.length > 4 && 
                    <div className = 'left' onClick={handleMoveLeft}>
                        <img data-original={'left button src'} alt ='picture load failed'/>
                   </div>
                }
                {   
                    imgList.length > 4 && 
                    <div className = 'right' onClick={handleMoveRight}>
                        <img data-original={'right button src'} alt ='picture load failed'/>
                   </div>
                }
            </div>
        }
    </div>
  )
}
export default Memo(AttachComponent);

AttachComponent.less

.attach-container{
    width:100%;
    position:relative;
    .img-list{
        width:100%;
        position:relative;
        padding:.1rem 0;
        .img-container{
            position:relative;
            left:0;
            transition:left 0.3s;
        }
        .img-item{
            display:inline-block;
            width:23.5%;
            margin-left:2%;
            height:1.3rem;
            img{
                width:100%;
                height:100%;
            }
            &:nth-of-type(1){
                margin-left:0;
            }
            &:nth-of-type(5n){
                margin-left:0;
            }
        }
    }
    .left, .right{
        width:.5rem;
        height:.5rem;
        background:rgba(0,0,0,0.3);
        border-raidus:50%;
        position:absolute;
        display:flex;
        justify-content:center;
        align-items:center;
    }
    .right{
        right:0;
    }
    ul,li{
        margin:0;
        padding:0;
        a{
            #427aff;
        }
    }
}
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 0 次点赞
  • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-04-08
个人主页被 585 人浏览