# Skeleton 骨架屏

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/skeleton/freepass)
- 平台：PC
- 维护者：蒋贝贝

## 代码演示

### 基础使用

```jsx
import React from 'react';
import { constants, Skeleton, Form, FormStore, FormInitAllDatas, FormDatas } from '@weapp/ui';

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

interface SkeletonDemoState {
  formDatas: FormDatas;
}
export default class SkeletonDemo extends React.Component<{}, SkeletonDemoState> {

  formStore = new FormStore();
  constructor(props: {}) {
    super(props);
    this.state = {
      formDatas: { avatar: "false", height: 300, singleHeight: 50, span: 1, displayType: 'columns' },
    }
    this.init();
  }
  init = () => {
    const allData = {
      data: { avatar: "false", height: 300, singleHeight: 50, span: 1 , displayType: 'columns'},
      groups: [],
      items: {
        height: { itemType: 'INPUTNUMBER' },
        singleHeight: { itemType: 'INPUTNUMBER' },
        span: { itemType: 'INPUTNUMBER' },
        leftNum: { itemType: 'INPUTNUMBER' },
        rightNum: { itemType: 'INPUTNUMBER' },
        avatar: { itemType: 'RADIO', data: [{ id: 'true', content: '是' }, { id: 'false', content: '否' }] },
        displayType: { itemType: 'SELECT', data: [{ id: 'row', content: '多行' }, { id: 'columns', content: '多列' }] },
      },
      layout: [
        [
          { id: 'height', label: '高度(height)', items: ['height'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'singleHeight', label: '单行高度(singleHeight)', items: ['singleHeight'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'span', label: '每行列数(span)', items: ['span'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'leftNum', label: '左侧列数(leftNum)', items: ['leftNum'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'rightNum', label: '右侧列数(rightNum)', items: ['rightNum'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'avatar', label: '是否展示头像(avatar)', items: ['avatar'], hide: false, labelSpan: 6 },
        ],
        [
          { id: 'displayType', label: '配置多行或多列(displayType)', items: ['displayType'], hide: false, labelSpan: 6 },
        ]
      ],
    } as FormInitAllDatas;
    this.formStore.initForm(allData);
  }
  onChange = (value?: FormDatas) => {
    this.setState({ formDatas: {
      ...this.state.formDatas,
      ...value,
    }})
  }
  render() {
    const { formDatas } = this.state;
    const height = formDatas.height as number;
    return (
      <div className={prefixCls}>
        <div className={`${prefixCls}-propSetting`}>属性设置</div>
        <div className={`${prefixCls}-propSetting-form`}>
          <Form store={this.formStore} onChange={this.onChange} />
        </div>
        <Skeleton {...formDatas} border skeletonHeight={height} avatar={formDatas.avatar === 'true'} />
      </div>
    )
  }
}
```

### 集成各组件内的效果

由各组件(如Table、Form、Comment)单独实现

```jsx
import React from 'react';
import { Dialog, Table, constants, ITableColumn, CardDetailForm, CardDetailFormStore, Form, FormStore, Comment, FormLayoutProps } from '@weapp/ui';
import { tableData, topFormData, cardDetailFormData } from './mockData';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

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

interface SkeletonDemoProps { }
interface SkeletonDemoState {
  loading: boolean;
  data: Array<any>;
  visible: boolean;
}
function getIndex(data: any, pos: any, name: any) {
  return <span>{pos.row + 1}</span>
}

function getName(data: any, pos: any, name: any) {
  return <a style={{ color: '#5D9CEC' }}>{data[name]}</a>
}

export default class SkeletonDemo extends React.Component<SkeletonDemoProps, SkeletonDemoState> {
  cardDetailFormStore = new CardDetailFormStore();
  topFormStore = new FormStore();
  constructor(props: SkeletonDemoProps) {
    super(props);
    this.state = {
      loading: false,
      data: [],
      visible: false,
    }
  }
  columns: ITableColumn[] = [
    {
      title: '序号',
      dataIndex: 'index',
      bodyRender: getIndex,
      textAlign: 'center',
      align: 'center',
      summaryRender: (data: any, column: any) => {
        return '合计'
      }
    }, {
      title: '项目名称',
      dataIndex: 'name',
      bodyRender: getName
    }, {
      title: '开始时间',
      dataIndex: 'start_time',
    }, {
      title: '结束时间',
      dataIndex: 'end_time',
    }, {
      title: '负责人',
      dataIndex: 'person',
      bodyRender: getName
    }
  ]
  componentDidMount() {
    this.setState({ loading: true }, () => {
      const mockApi = setTimeout(() => {
        this.setState({ data: tableData, loading: false });
        clearTimeout(mockApi);
      }, 500);
    })
  }
  textareaProps = { autoFocus: false };
  uploadProps = {
    secretProtect: true,
    secretLevel: '1',
    uploadParams: {
      module: 'document'
    }
  };
  onRowClick = (data: any) => {
    this.setState({ visible: true });
    const mockDataApi = setTimeout(() => {
      this.topFormStore.initForm({ ...topFormData, data });
      this.cardDetailFormStore.init(cardDetailFormData);
      clearTimeout(mockDataApi);
    }, 1800);
  }
  onClose = () => {
    this.topFormStore.clear(); // clear 卸载store内的数据，重置store，卸载后，再次初始化才可再次调用骨架屏效果
    this.cardDetailFormStore.clear();
    this.setState({ visible: false });
    // this.topFormStore.setState({ isFormInit: false });
  }
  customRenderFormSwitch = (id: string) => {
    return <div>自定义组件测试区域</div>
  }
  customHide = (col: FormLayoutProps) => {
    const datas = this.topFormStore.getFormDatas();
    if (datas.select === '1' && col.id === 'name') return { // 涉及表单初始化字段的联动效果，动画高度无法计算准确，数据回显在动画计算后完成，因为会出现高度回缩的情况
      ...col,
      hide: true,
    }
    return col;
  }
  render() {
    const { loading, data, visible } = this.state;
    return (
      <div className={prefixCls}>
        <Table
          columns={this.columns}
          data={data}
          loading={loading}
          useSkeleton
          onRowClick={this.onRowClick}
        />
        <Dialog
          title="测试demo"
          visible={visible}
          onClose={this.onClose}
          noMaskClose
          closable
          placement="right"
        // destroyOnClose
        >
          <CardDetailForm.Title
           
            url={imgSrc}
            className="testCardDetailFormTitle"
            store={this.cardDetailFormStore}
            enableMultilineText
            textareaProps={this.textareaProps}
            useSkeleton
          />
          <Form
           
            store={this.topFormStore}
            useSkeleton
            customRenderFormSwitch={this.customRenderFormSwitch}
            skeletonHeight={40} // 设置自定义组件区域默认高度和
            customHide={this.customHide}
          />
          <CardDetailForm
           
            store={this.cardDetailFormStore}
            useSkeleton
          // openLoading
          />
          <div className={`${prefixCls}-comment`}>
            <Comment
              module={"document"}
              objectModule="doc"
              targetId="1362372473190223857"
              uploadProps={this.uploadProps}
              useSkeleton
            />
          </div>
        </Dialog>
      </div>
    )
  }
}
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| avatar | 是否展示头像 | boolean | 否 | - |
| border | 是否配置边框 | boolean | 否 | false |
| displayType | 配置多行或多列 | "row" \| "column" | 否 | - |
| leftNum | 左侧列数 | number | 否 | - |
| rightNum | 右侧列数 | number | 否 | - |
| skeletonHeight | 高度 | number | 否 | - |
| skeletonItemHeight | 单行高度 | number | 否 | - |
| span | 每行列数(目前仅支持一行一列及一行两列) | number | 否 | - |
### Form类组件(Form、CardDetailForm)骨架屏特殊说明

##### 一、自定义组件

自定义组件的高度Form无法计算，因此在使用时，遇到自定义组件，需估算自定义组件默认展示高度志和传入Form类组件内。参数为skeletonHeight。

##### 二、动画效果

1.在组件首次加载时会默认触发
2.在手动调用clear方法卸载后，再次initForm后触发，具体见Skeleton的Demo(集成各组件内的效果)
