antd design pro前端填坑笔记

1、react组件调用dispatch页面不渲染

state参数是引用类型,如果直接返回state,dva会认为没有修改state,所以不会刷新
建议

return {...state}

2、github/gitlab 拉取合并请求代码

通过git拉取github/gitlab上的Pull Request(PR)/Merge Request(MR)到本地进行code review
Github:
git fetch origin pull/3188/head:pr3188
3188PR的id
https://github.com/apache/carbondata/pull/3188

Gitlab:
git fetch remote merge-requests/MERGE_REQUEST_ID/head:BRANCH_NAME

修改别人提交的代码(合并和修改commit)

1、合并commit

git rebase -i  COMMIT_ID(修改的上一个ID)

2、修改最后提交的commit

git commit --amend 修改最后提交的commit

3、代码回退
a) 在未add之前,直接git checkout file_name即可;
b)在提交之后,通过git reset --hard commit_id即可

暂存区处理

1、git stash
这个指令会把所有未提交的修改都保存起来。
2、git stash pop
将缓存堆中的第一个stash删除,并将对应修改应用到当前目录下。
3、git stash apply
将指定缓存堆栈中的stash应用到工作目录中,默认是 stash@{0},但并不会删除stash拷贝
4、git stash drop
删除指定的stash
5、git stash list
查看所有的暂存区
6、git stash clear
清除所有的暂存区
7、git stash show
查看stash修改情况,默认为stash@{0},加-p查看特定stash的全部diff,
8、git stash branch + 分支名
如果你储藏了一些工作,暂时不去理会,然后继续在你储藏工作的分支上工作,你在重新应用工作时可能会碰到一些问题。如果尝试应用的变更是针对一个你那之后修改过的文件,你会碰到一个归并冲突并且必须去化解它。如果你想用更方便的方法来重新检验你储藏的变更,你可以运行 git stash branch,这会创建一个新的分支,检出你储藏工作时的所处的提交,重新应用你的工作,如果成功,将会丢弃储藏。

3、组件样式覆盖

对组件添加一层div标签,并将组件样式写在div样式里面,防止样式全局污染,例如,Input` 自带的类名为 input-style,定义新的类名在div上,这会覆盖div内部Input样式

// Input 组件
import styls from './input.less'
<div className={style["input-bx"]}>
<Input/>
</div>
.input-box :global{
	.input-style{
    	background-color: pink;
    }
}

4、不同命名空间组件相互调用

对于不同命名空间的组价调用,会报错找不到命名空间,出现这种问题的原因是model service mock 不是全局的,将组件下面的这三个文件分别剪切放在全局的三个文件夹中即可。

5、antd 组件图片模拟上传测试

直接上代码,主要参考antd组件上传图片代码API

index.jsx文件

import React, {useState, useEffect} from 'react';
import request from "@/utils/request";
import {EyeOutlined, DeleteOutlined, PlusOutlined} from '@ant-design/icons';
import {Upload, Button, Modal, message} from 'antd';
import styles from './style.less';

const getBase64 = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onload = () => resolve(reader.result);
    reader.onerror = error => reject(error);
  });
}

const maxNum = 8;

const Avatar = props => {
  const [fileList, setFileList] = useState([]);
  const [showImg, setShowImg] = useState([]);
  const [previewVisible, setPreviewVisible] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [previewImage, setPreviewImage] = useState("");
  const [previewTitle, setPreviewTitle] = useState("");

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

  // 获取图片
  const getImgAll = () => {
    request(`/api/sys/img/get`, {
      method: 'POST',
    }).then(res => {
      if (res.status === 'ok') {
        setShowImg(res.data);
      } else {
        message.error("获取图片失败!")
      }
    }).catch(err => {
      throw err;
    })
  }

  // 开始上传图片
  const handleUpload = async (e) => {
    e.stopPropagation();
    setUploading(true);

    for (let i = 0; i < fileList.length; i++) {
      fileList[i] = {
        lastModified: fileList[i].lastModified,
        name: fileList[i].name,
        size: fileList[i].size,
        type: fileList[i].type,
        uid: fileList[i].uid,
        webkitRelativePath: fileList[i].webkitRelativePath,
        url: await getBase64(fileList[i].originFileObj)
      };
    }
    request(`/api/sys/img/upload`, {
      method: 'POST',
      data: fileList,
    }).then(res => {
      setUploading(false);
      setFileList([]);
      message.destroy();
      message.success({content: '上传成功.', duration: 1, onClose: () => getImgAll()});
    }).catch(err => {
      setUploading(false);
      message.destroy();
      message.error('上传失败.');
    })
  }

  //删除已经上传图片
  const onRemoveImg = file => {
    message.destroy();
    request(`/api/sys/img/delete`, {
      method: 'POST',
      data: file,
    }).then(res => {
      if (res.status === 'ok') {
        getImgAll();
        message.info("删除成功!");
      } else {
        message.error("删除失败!")
      }
    }).catch(err => {
      throw err;
    })
  }
  // 删除未上传图片
  const onRemove = file => {
    const index = fileList.indexOf(file);
    const newFileList = fileList.slice();
    newFileList.splice(index, 1);
    setFileList(newFileList);
  }

  const handlePreview = async file => {
    if (!file.url && !file.preview) {
      file.preview = await getBase64(file.originFileObj);
    }
    setPreviewImage(file.url || file.preview);
    setPreviewVisible(true);
    setPreviewTitle(file.name || file.url.substring(file.url.lastIndexOf('/') + 1));
  };

  const ImgBox = props => {
    const {file} = props;
    return (
      <div className={styles["gl-box"]}>
        <img className={styles["gl-img"]} src={file.url} alt=""/>
        <div className={styles["img-box"]}>
          <EyeOutlined onClick={() => handlePreview(file)} className={styles["gl-icon"]}/>
          <DeleteOutlined onClick={() => onRemoveImg(file)} className={styles["gl-icon"]}/>
        </div>
      </div>
    )
  };

  return (
    <div>
      <div key="img-box">
        {
          showImg.map((i, j) => <ImgBox key={j} file={i}/>)
        }
      </div>
      {
        showImg.length >= maxNum ?  `文件仅支持上传${maxNum}张图片,若更改请删除后重新上传。` :
          (<Upload
            onRemove={(file) => onRemove(file)}
            beforeUpload={(file) =>false}
            fileList={fileList}
            listType="picture-card"
            onPreview={(file) => handlePreview(file)}
            onChange={(info) => {
              if (info.fileList.length + showImg.length > maxNum) {
                message.destroy();
                message.warning(`仅支持上传${maxNum}张图片`)
              }
              setFileList(info.fileList.splice(0,maxNum-showImg.length));
            }}
          >
            <div><PlusOutlined/></div>
            <Button
              size={"small"}
              type="primary"
              onClick={(e) => handleUpload(e)}
              disabled={fileList.length === 0}
              loading={uploading}
              style={{position: "relative", bottom: -20}}
            >
              {uploading ? '...' : '上传'}
            </Button>
          </Upload>)}

      <Modal
        visible={previewVisible}
        title={previewTitle}
        footer={null}
        onCancel={() => setPreviewVisible(false)}
      >
        <img alt="example" style={{width: '100%'}} src={previewImage}/>
      </Modal>

    </div>
  );
}


export default Avatar;

style.less文件


.gl-box{
  display:inline-block;
  height: 105px;
  width: 105px;
  border:1px solid #eee;
  margin: 5px;
  overflow: hidden;
  text-align: center;
}
.gl-icon{
  font-size:16px;
  color:#eee;
  margin: 0 5px;
}

.gl-img{
  height: 104px;
  width: 104px;
  z-index:1;
  cursor: pointer;

}
.img-box {
  height: 104px;
  width: 104px;
  position: relative;
  line-height: 104px;
  bottom: 0;
  z-index: 2;
  background-color: rgba(0, 0, 0, 0.5);
  vertical-align: middle;
}

.gl-box:hover{
  .img-box{
    transition: all .3s ease;
    transform: translateY(-104px);
  }
}

全局mock文件

(放在全局mock文件里面即可)
img.js

let imgList = [
  {
    url:'https://i01piccdn.sogoucdn.com/905894db522971e6',
    lastModified: 1591877022945,
    name: "direwolf.jpg",
    size: 41033,
    type: "image/jpeg",
    uid: "rc-upload-1592210832893-2",
    webkitRelativePath: "",
  },
]
const imgData = (req, res) => {
  imgList = imgList.concat(req.body);//imgList=[...imgList,...req.body]
  res.json({status: 'ok', data: req.body});
}
const getData = (req, res) => {
  res.json({status: 'ok', data: imgList});
}
const deleteData = (req, res) => {
  imgList.splice(imgList.indexOf(imgList.filter(i=>i.url===req.body.url)[0]), 1);
  res.json({status: 'ok', data: imgList});
}
export default {
  'POST /api/sys/img/upload': imgData,
  'POST /api/sys/img/get': getData,
  'POST /api/sys/img/delete': deleteData,
};

6、自定义可编辑单元格组件不渲染问题

对于业务需求需要对单元格内容可编辑,并对组件实时渲染,例如自己封装的Checkbox组件如下,具体功能是根据操作实现对列数据的展示效果。

import React,{useState} from 'react';
import {Checkbox} from "antd";

const CheckboxCom=props=>{
// row,text  为表格的row 行数据和值
// type 是自定义属性,判断是否固定列 是否显示列等 例如是 fixed
  const {row,onChangeItem,type,text} = props;
  const [ch,setChecked] = useState(undefined);
  const onChange = value=>{
    setChecked(value);
    row[type] = value;
    onChangeItem(row);
  }
  return (<Checkbox onChange={e=>onChange(e.target.checked)} checked={ch||text} />)
}

export default CheckboxCom;

当组件渲染的时候,组件会根据表格数据,动态绑定值。
但是在设置恢复默认设置的时候,并未渲染组件。通过判断ch是否存在来回填checked 就可以解决问题了,也就是在没有改变值的情况下,checked充当了 defaultValue 的作用。

7、自定义表格最后一行渲染

summary属性中定义antd 表格的汇总行
官网

/**
 * Created by lidianzhong on 2020-07-08.
 * To: More pain, more gain.
 */

import React, {useState} from 'react';
import {Table, Pagination, Checkbox} from 'antd';
import {SearchOutlined, PlusCircleTwoTone, MinusCircleTwoTone} from '@ant-design/icons';
import Mock from "mockjs";


const dataSource = Array(100)
  .fill(0, 0, 100)
  .map((item, i) => {
    return Mock.mock({
      index: i + 1,
      key: i + 1,
      "name|1": ['胡彦斌', "kankan", "kankan1"],
      "money|1-100": 32,
      address: Mock.mock("@city()"),
      address0: Mock.mock("@city()"),
      address1: Mock.mock("@city()"),
      address2: Mock.mock("@city()"),
      address3: Mock.mock("@city()"),
      address4: Mock.mock("@city()"),
      address5: Mock.mock("@city()"),
      address6: Mock.mock("@city()"),
    })
  });

const defaultPageSize = 20;
const TableComponents = () => {

  const [filteredInfo, setFilteredInfo] = useState({});
  const [sortedInfo, setSortedInfo] = useState({});
  const [data, setData] = useState([]);

  useState(() => {
    queryFun(1, defaultPageSize);
  }, [])


  const onHeaderRow = (column, index) => {
    console.log(column, index);
  }
  const onRow = (column, index) => {
    // console.log(column, index);
  }
  const children1=[];
  for (let i = 0; i < 7; i++) {
    children1.push({
      title: '住址' + i, dataIndex: 'address' + i, key: 'address' + i, width: 200
    });
  }
  let columns = [
    {
      title: "序号",
      dataIndex: 'index',
      key: 'index',
      width: 100,
      fixed: 'left',
    },
    {
      title: <span>姓名<Checkbox/></span>,
      dataIndex: 'name',
      key: 'name',
      width: 100,
      align: 'center',
      ellipsis: true,
      className: 'kankan',
      // colSpan:1,
      // defaultFilteredValue:'kankan',
      // defaultSortOrder:'ascend',
      // filterDropdownVisible:true,
      // filtered:true,
      filterMultiple: true,
      // filterDropdown:()=><div>filterDropdown</div>,

      onCell: (record, rowIndex) => {
        return {
          onClick: event => {
            console.log("onCell单击行触发")
          }, // 点击行
          onDoubleClick: event => {
            console.log("onCell双击行触发")
          },
          onContextMenu: event => {
            console.log("onCell菜单触发")
          },
          onMouseEnter: event => {
            console.log("onCell鼠标移入触发")
          }, // 鼠标移入行
          onMouseLeave: event => {
            console.log("onCell鼠标移出触发")
          },
        };
      },
      // onFilter:(e)=>{},
      // onFilterDropdownVisibleChange:()=>{},
      onHeaderCell: column => {
        return {
          onClick: event => {
            console.log("onHeaderCell单击行触发")
          }, // 点击行
          onDoubleClick: event => {
            console.log("onHeaderCell双击行触发")
          },
          onContextMenu: event => {
            console.log("onHeaderCell菜单触发")
          },
          onMouseEnter: event => {
            console.log("onHeaderCell鼠标移入触发")
          }, // 鼠标移入行
          onMouseLeave: event => {
            console.log("onHeaderCell鼠标移出触发")
          },
        };
      },
      filters: [
        {text: 'kankan', value: 'kankan'},
        {text: 'kankan1', value: 'kankan1'},
        {text: '胡彦斌', value: '胡彦斌'},
      ],


      filteredValue: filteredInfo.name || null,
      onFilter: (value, record) => record.name.includes(value),
    },
    {
      title: '身价', dataIndex: 'money', key: 'money', width: 100,
      defaultSortOrder: 'ascend',
      sortDirections: ["ascend", "descend"],
      showSorterTooltip: true,
      sorter: (a, b) => a.money - b.money,
      sortOrder: sortedInfo.columnKey === 'money' && sortedInfo.order,

    },
    {
      title: '住址', children: [
        {title: '住址', dataIndex: 'address1', key: 'address1', width: 200},
        ...children1,
      ]
    },
  ];

  const columnss = columns.reduce((total,curr,index,arr)=>{
    if(curr.children){
      return total.concat(curr.children);
    }else{
      return total.concat(curr);
    }
  },[]);
  console.log(columnss);


  /**
   * @fun self-define summery.
   * @param currentData
   * @returns {*}
   */
  const onSummary = (currentData) => {
    const a = {
      index: '总计',
      key:"total",
      name: '',
      money: 0,
      address: '',
      address0: "",
      address1: "",
      address2: "",
      address3: "",
      address4: "",
      address5: "",
      address6: "",
    };
    const data = currentData.reduce((total, currentValue, currentIndex, arr) => {
      return {
        ...a,
        money: total.money + currentValue.money,
      }
    }, a)

    return <>
      <Table.Summary.Row>
        <Table.Summary.Cell key={0} index={0}/>
        {columnss.map((i, j) =><Table.Summary.Cell key={j+1} index={j+1}>{data[i.dataIndex]}</Table.Summary.Cell>)
        }
      </Table.Summary.Row>
    </>
  }

  const titleFunction = enable => {
    return enable[0].name + " " + enable[0].address;
  }

  const expandableSelect = {
    rowExpandable: record => record.name !== 'kankan1',
    childrenColumnName: '展开',
    defaultExpandAllRows: false,
    // defaultExpandedRowKeys: [1],
    expandIcon: ({expanded, onExpand, record}) => {
      //https://codesandbox.io/s/fervent-bird-nuzpr?file=/index.js:1450-1677
      return expanded ? (
        <MinusCircleTwoTone onClick={e => onExpand(record, e)}/>
      ) : (
        <PlusCircleTwoTone onClick={e => onExpand(record, e)}/>
      )
    },
    expandIconColumnIndex: 0,
    // expandedRowKeys: [2,6],
    expandedRowRender: (record, index, indent, expanded) => {
      // console.log(record, index, indent, expanded);
      return <div style={{backgroundColor: 'pink'}}>
        <p>record:{record.name}</p>
        <p>index:{index}</p>
        <p>indent:{indent}</p>
        <p>expanded:{expanded}</p>
      </div>
    },
    indentSize: 20,
    expandRowByClick: true,
    onExpand: (expanded, record) => {
      console.log(expanded, record);
    },
    onExpandedRowsChange: (expandedRows) => {
      console.log("expandedRows", expandedRows)
    }

  }


  const handleChange = (pagination, filters, sorter) => {
    console.log('Various parameters', pagination, filters, sorter);
    setFilteredInfo(filters);
    setSortedInfo(sorter);
  };

  function queryFun(page, size) {
    setData([...dataSource].splice((page - 1) * size, size));
  }

  return (<div style={{padding: 50}}>
    <Table
      // title={(currentPageData) => titleFunction(currentPageData)}
      dataSource={data}
      columns={columns}
      showHeader={true}
      bordered={true}
      summary={(currentData) => onSummary(currentData)}
      scroll={{x: 800, y: 400}}
      size={'small'}
      onChange={(pagination, filters, sorter) => handleChange(pagination, filters, sorter)}
      // onHeaderRow={(column, index)=> onHeaderRow(column, index)}
      // onRow={(column, index)=> onRow(column, index)}
      onRow={(record, index) => {
        return {
          onClick: event => {
            console.log("单击行触发")
          }, // 点击行
          onDoubleClick: event => {
            console.log("双击行触发")
          },
          onContextMenu: event => {
            console.log("菜单触发")
          },
          onMouseEnter: event => {
            console.log("鼠标移入触发")
          }, // 鼠标移入行
          onMouseLeave: event => {
            console.log("鼠标移出触发")
          },
        };
      }}
      onHeaderRow={column => {
        return {
          onClick: () => {
            console.log("点击表头行触发", column);
          }, // 点击表头行
        };
      }}
      // sortDirections={["ascend","descend"]}
      // showSorterTooltip={true}
      expandable={expandableSelect}
      pagination={false}
    />
    <Pagination
      style={{float: 'right'}}
      onChange={(page, pageSize) => {
        queryFun(page, pageSize);
      }}
      onShowSizeChange={(current, size) => {
        queryFun(current, size);
      }}
      total={dataSource.length}
      showSizeChanger
      showQuickJumper
      pageSizeOptions={[10, 20, 50, 100, 200]}
      defaultCurrent={1}
      defaultPageSize={defaultPageSize}
      showTotal={total => `总计 ${total} 条`}
    />
  </div>)
}

export default TableComponents;

挖坑,填坑持续更新中。。。

同时也希望大家留言评论。。。

干了这碗酒

寄语

关于具体逻辑,在代码注释中已经给出。想学东西,希望读懂每一行代码,而不是简单的复制看效果。

不为别人,只为自己也要变得优秀。

主页

冰原狼主页:https://kankan.fun/
CSDN主页:https://blog.csdn.net/qq_38025939/
Github主页:https://github.com/kankanol1

明天的你一定会感谢现在拼命的自己!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值