# Board 看板

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/board/freepass)
- 平台：PC
- 维护者：赵俊飞
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[Board]

## 代码演示

### 基础用法

直接引入组件进行使用，

```jsx
import { Board, constants, ListData } from '@weapp/ui';
import React from 'react';
import { mockData } from '../mock';
const prefixCls = `${constants.uiPcClsPrefix}-demo-board`;

export default class BoardDemo extends React.PureComponent {
  state = {
    data: [...mockData],
    ListOptions: [{
      id: "list1",
      loading: true,
      hasMore: true,
      paginationProps: {},
      onScroll: (e: Event) => {
        console.log("scroll", e)
      }
    }]
  }
  onListSortEnd = (data: ListData[], moveItem: any, fromIndex: number | undefined, toIndex: number | undefined) => {
    console.log('onListSortEnd', data, moveItem, fromIndex, toIndex)
  }

  onCardSortEnd = (data: ListData[], moveItem: any, from: any, to: any) => {
    console.log(data, moveItem, from, to)
    this.setState({ data: [...data] });
  }

  render() {

    const { ListOptions } = this.state;

    return (
      <div>
        <h3>第三个看板被固定</h3>
        <Board
         
          height={400}
          data={this.state.data}
          onCardClick={console.log}
          className={`${prefixCls} ${prefixCls}-custom1`}
          onListSortEnd={this.onListSortEnd}
          onCardSortEnd={this.onCardSortEnd}
          lockEl="board-lock-pannel" //被锁定的元素的calssName
          ListOptions={ListOptions}
        />
      </div>

    );
  }
}
```

### 自定义渲染看板卡片

```jsx
import { Avatar, Board, constants, Icon, ListData } from '@weapp/ui';
import React from 'react';

const prefixCls = `${constants.uiPcClsPrefix}-demo-board`;

export default class BoardDemo extends React.PureComponent {
  state = {
    data: []
  }

  componentDidMount() {
    this.setState({ data: [...this.getData()] })
  }

  getData = () => {
    const mockData = [
      {
        id: '1',
        title: this.customListTitleRender('新功能需求'),
        cards: [
          {
            id: '11',
            title: 'e10看板功能需求，基础功能展示000',
            dueDate: '2021-3-29',
          },
          { id: '12', title: 'e10看板功能需求111', dueDate: '2021-3-10' },
        ],
      },
      {
        id: '2',
        title: this.customListTitleRender('开发中'),
        cards: [
          { id: '21', title: 'e10看板功能需求222', dueDate: '2021-3-29' },
          { id: '22', title: 'e10看板功能需求333', dueDate: '2021-4-10' },
        ],
      },
      {
        id: '3',
        title: this.customListTitleRender('提交测试'),
        cards: [
          { id: '31', title: 'e10看板功能需求444', dueDate: '2021-4-01' },
          { id: '32', title: 'e10看板功能需求555', dueDate: '2021-3-29' },
          { id: '33', title: 'e10看板功能需求666', dueDate: '2021-3-10' },
          { id: '34', title: 'e10看板功能需求777', dueDate: '2021-4-10' },
          { id: '35', title: 'e10看板功能需求888', dueDate: '2021-3-29' },
          { id: '36', title: 'e10看板功能需求999', dueDate: '2021-3-29' },
          { id: '37', title: 'e10看板功能需求10', dueDate: '2021-3-29' },
          { id: '38', title: 'e10看板功能需求11', dueDate: '2021-3-29' },
        ],
      },
    ];

    return mockData
  }

  getBorderColor = (date: string) => {
    switch (date) {
      case '2021-3-10':
        return '#d45172';
      case '2021-3-15':
        return '#d45172';
      case '2021-3-29':
        return '#6dab39';
      default:
        return 'transparent';
    }
  };

  customCardRender = (item: any) => {
    return (
      <div
        className={`${prefixCls}-card`}
        style={{ borderLeft: `4px solid ${this.getBorderColor(item.dueDate)}` }}
      >
        <div>{item.title}</div>
        <div className={`${prefixCls}-card-content`}>
          <span>
            <Icon name={'Icon-Unfinished-business-o'} />
            <span>{item.dueDate}</span>
          </span>
          <span>
            <Avatar
             
              size='small'
              name='杨文元'

            ></Avatar>
          </span>
        </div>
      </div>
    );
  };

  customListTitleRender = (title: string) => {
    return (
      <div className={`${prefixCls}-title`}>
        <span>{title}</span>
        <Icon name='Icon-more-o' />
      </div>
    );
  };

  onListSortEnd = (data: ListData[]) => {
    console.log('onListSortEnd', data)
  }

  onCardSortEnd = (data: ListData[], moveItem: any, from: any, to: any) => {
    console.log(data, moveItem, from, to)
    this.setState({ data: [...data] })
  }

  render() {
    return (
      <Board
       
        height={400}
        data={this.state.data}
        customCardRender={this.customCardRender}
        onCardSortEnd={this.onCardSortEnd}
        onListSortEnd={console.log}
        onCardClick={console.log}
      />
    );
  }
}
```

### 自定义渲染看板列表

```jsx
import { Avatar, Board, constants, Icon, Popover, ListData } from '@weapp/ui';
import { toJS } from 'mobx';
import React, { ReactNode } from 'react';

const prefixCls = `${constants.uiPcClsPrefix}-demo-board`;

const mockData = [
  {
    id: 'list1',
    title: '新功能需求',
    cards: [
      {
        id: 'card0',
        title: 'e10看板功能需求，基础功能展示0',
        dueDate: '2021-3-29',
      },
      { id: 'Card1', title: 'e10看板功能需求1', dueDate: '2021-3-10' },
    ],
  },
  {
    id: 'list2',
    title: '开发中',
    cards: [
      { id: 'Card2', title: 'e10看板功能需求2', dueDate: '2021-3-29' },
      { id: 'Card3', title: 'e10看板功能需求3', dueDate: '2021-4-10' },
    ],
    canCardAdd: true,
  },
  {
    id: 'list4',
    title: '提交测试',
    cards: [
      { id: 'Card5', title: 'e10看板功能需求4', dueDate: '2021-4-01' },
      { id: 'Card6', title: 'e10看板功能需求5', dueDate: '2021-3-29' },
      { id: 'Card7', title: 'e10看板功能需求6', dueDate: '2021-3-10' },
      { id: 'Card8', title: 'e10看板功能需求7', dueDate: '2021-4-10' },
      { id: 'Card9', title: 'e10看板功能需求8', dueDate: '2021-3-29' },
    ],
    canCardAdd: true,
  },
];

const moreBtnData = [
  { id: 'tab1', content: '操作1' },
  {
    id: 'tab2',
    content: '操作2',
  },
  { id: 'tab3', content: '操作3' },
];
export default class BoardDemo extends React.PureComponent {
  state = {
    data: mockData,
    timeStamp: new Date(),
  };

  getBorderColor = (date: string) => {
    switch (date) {
      case '2021-3-10':
        return '#d45172';
      case '2021-3-15':
        return '#d45172';
      case '2021-3-29':
        return '#6dab39';
      default:
        return 'transparent';
    }
  };

  onListChange = (item: any) => {
    const { data } = this.state;
    const index = data.findIndex((d) => d.id === item.id);
    const newData = toJS(data);
    newData[index] = item;
    this.setState({ data: newData, timeStamp: new Date() });
  };

  onCardSortEnd = (data: ListData[], moveItem: any, from: any, to: any) => {
    console.log(data, moveItem, from, to)
    this.setState({ data: [...data] })
  }

  /**
   * 渲染列表中的每一项
   * @param item any
   * @returns JSX.Element
   */
  customCardRender = (item: any, index: number, reactNode: ReactNode) => {

    return (
      <>
        <div
          className={`${prefixCls}-card`}
          style={{ borderLeft: `4px solid ${this.getBorderColor(item.dueDate)}` }}
        >
          <div style={{ display: "flex" }}>
            <span style={{ margin: "0 5px", color: "#f05b72" }}>{index}</span>
            <div style={{ width: "100%" }}>
              <div>{item.title}</div>
              <div className={`${prefixCls}-card-content`}>
                <span>
                  <Icon name={'Icon-Unfinished-business-o'} />
                  <span>{item.dueDate}</span>
                </span>
                <span>
                  <Avatar
                   
                    size='small'
                    name='杨文元'
                  ></Avatar>
                </span>
              </div>
            </div>
          </div>

        </div>
        <div className={`${prefixCls}-break`}></div>
      </>
    );
  };

  /**
   * 渲染整个列表
   * @param item any
   * @param children any
   * @returns JSX.Element
   */
  customListRender = (item: any, children: any) => {
    return (
      <CustomBoardListDemo
       
        onChange={this.onListChange}
        item={item}
        children={children}
      />
    );
  };

  render() {
    const { data } = this.state;
    return (
      <Board
        className={`${prefixCls} ${prefixCls}-custom3`}
       
        height={400}
        data={toJS(data)}
        customCardRender={this.customCardRender}
        customListRender={this.customListRender}
        onCardSortEnd={this.onCardSortEnd}
        onListSortEnd={console.log}
        onCardClick={console.log}
      />
    );
  }
}

export class CustomBoardListDemo extends React.PureComponent<any> {
  renderMoreBtn = () => {
    return (
      <Popover options={moreBtnData}>
        <Icon name='Icon-more-o' />
      </Popover>
    );
  };

  onAddClick = () => {
    const { item, onChange } = this.props;

    const newCard = {
      id: 'card10',
      title: '新增任务' + item.cards.length,
      dueDate: '2021-4-29',
    };

    const cards = item.cards;
    cards.push(newCard);
    item.cards = cards;
    onChange(toJS(item));
  };

  onCardSortEnd = (data: ListData[], moveItem: any, from: any, to: any) => {
    console.log(data, moveItem, from, to)
    this.setState({ data: [...data] })
  }

  render() {
    const { item, children } = this.props;
    return (
      <div>
        <div className={`${prefixCls}-list-custom`}>
          <div className={`${prefixCls}-list-title`}>
            <div className={`${prefixCls}-title`}>
              <span>{item.title}</span>
              {this.renderMoreBtn()}
            </div>
          </div>
          <div className={`${prefixCls}-break`}></div>
          {children}
        </div>
      </div>
    );
  }
}
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| height | 高度 | number \| string | 否 | auto |
| width | 宽度 | number \| string | 否 | 100% |
| data | 看板数据 | BoardListItem[] | 否 | [] |
| listOptions | 通过id配置每个看板的list属性 | ListProps[]; | 否 |  |
| draggable | 看板列表是否可拖拽位置 | boolean | 否 | true |
| lockEl | 被锁定看板的class | string | 否 |  |
| customListRender | 看板列表自定义渲染 | function({ listItem }) | 否 |  |
| customCardRender | 看板卡片自定义渲染 | (data: ListData, index: number, card: ReactNode) => ReactNode | 否 |  |
| onListSortEnd | 看板列表拖拽事件 | function({ items }) | 否 |  |
| onCardSortEnd | 看板卡片拖拽事件 | function({ items }) | 否 |  |
| onCardClick | 看板卡片点击事件 | function({ item }) | 否 |  |
| disabledScroller | 去掉横向滚动 | boolean | 否 | false |
| onMove | 卡片拖拽回调 | function(evt, originEvt) | 否 |  |
### BoardListItem

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| title | 看板列表标题 | string \| ReactNode | 否 | "" |
| cards | 看板数据 | BoardCardItem[] | 否 | [] |
| cardDraggable | 看板卡片是否可拖拽位置 | boolean | 否 | true |
| CardSortableOptions | 自定义 list 拖拽（拖拽相关的方法,参考 sortablejs 文档） | object | 否 |  |
| oolean | 否 | true |  |  |
| SortableOptions | 自定义 card 拖拽（拖拽相关的方法,参考 sortablejs 文档） | object | 否 |  |
### BoardCardItem

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| title | 看板卡片标题 | string \| ReactNode | 否 | "" |
| description | 看板卡片描述 | string \| ReactNode | 否 | "" |
| content | 看板卡片详情 | ReactNode | 否 |  |
