预期目标

我自己随便写了一些mock data,格式是json:

[
  {"Name": "Tom", "Team": "Tom & Jerry", "Sex": "male"},
  {"Name": "Lei Li", "Team": "English Learning", "Sex": "male"},
  {"Name": "Meimei Han", "Team": "English Learning", "Sex": "female"},
  {"Name": "Jerry", "Team": "Tom & Jerry", "Sex": "male"}
]

为了保证后续代码的严谨性,我这里先将组别乱序,防止到时候Grouping功能没有生效,但效果还是一样。

我们的目标是做一个有简单交互界面,界面主要就两个组件:

  1. 右侧复选框:选择性别,以展示选中内容
  2. 左侧内容区域:以Team分组,分别显示人名

我简单用Mockplus绘制了一下原型图,就是这样一个简单的效果。右边我计划使用简单的文字形式呈现,因为Fluent UI的DetailsList要写的代码还是不少的。

typescript 有bytes类型么_栅格系统

编写组件

这里说明的一下,因为Mobx的原因,我初步的教程是使用Class Component进行讲解,后续有时间的话,我会添加最新的Function Component写法。

我们已经有了原型图了,怎么样设计一个编写代码的结构呢?我强烈建议大家以后可以研读一下官网的《React哲学》,这篇文章很好地展示了React的一个开发流程,尤其是如何确定state的说明,虽然我们这边将会用MobX代替state。

typescript 有bytes类型么_栅格系统_02

可以看到我将这个页面分为三个模块,红色区域为整个大组件将会包含两个子组件。那么很显然,我将会主要编写三个组件,红色区域的整体控件Canvas_Class.tsx,绿色的信息控件DetailsInfo_Class.tsx,蓝色的多选框CheckBox_Class.tsx

首先在src文件夹下创建文件夹Component,然后创建这三个文件。

准备工作

Fluent UI的一些组件(比如CheckBox)会依赖于它自身的图标库,因此我们需要先初始化一下图标,官方文档在这

我们修改一下index.tsx,添加initializeIcons,这个方法建议在最顶级的那一层添加,所以我才放在index.tsx。同时,它需要放在render前面。

import { Canvas } from './Components/Canvas_Class';
import React from 'react';
import ReactDOM from 'react-dom';
import { initializeIcons } from '@fluentui/react/lib/Icons';

initializeIcons();

ReactDOM.render(
  <React.StrictMode>
    <Canvas/>
  </React.StrictMode>,
  document.getElementById('root')
);

这里Canvas会报错,不过别担心,后面就会写。

整体控件Canvas

Fluent UI也有自己的栅格系统。这里我们使用一下栅格系统简单布局。

但是栅格系统是依赖于Fabric Core的,我们需要添加一下相关环境,配置文档在这

打开public文件夹下的index.html,添加一行

<link
  rel="stylesheet"
  href="https://static2.sharepointonline.com/files/fabric/office-ui-fabric-core/11.0.0/css/fabric.min.css"
/>

然后在body标签上添加一下类

<body class="ms-Fabric" dir="ltr">
</body>

当然,也可以选择使用npm添加,具体操作就看上面的官方文档即可。

然后我们简单使用栅格系统来划一下区域,根据屏幕大小,可以使组件自动调整位置。当然记得在class前添加一下export,不然没法在外部使用这个组件。

import * as React from 'react';

import {CheckBoxSex} from './CheckBox_Class';
import {DetailsInfo} from './DetailsInfo_Class';

export class Canvas extends React.Component {

    public render() {
        return (
            <div className="ms-Grid" dir="ltr">
                <div className="ms-Grid-row">
                    <div className="ms-Grid-col ms-sm6 ms-md4 ms-lg3">
                        <DetailsInfo />
                    </div>
                    <div className="ms-Grid-col ms-sm6 ms-md8 ms-lg9">
                        <CheckBoxSex />
                    </div>
                </div>
            </div>
        );
    }
}

复选框CheckBox

我们进入Fluent UI官网搜索自己想要的控件,这里选择了Checkbox

typescript 有bytes类型么_控件_03

巨硬还是很贴心地给了源代码(我等CV工程师福音),点击右上角Show Code就可以查看,或者点击Export to CodePen就可以在线编辑了。上面提到过Class Component和Function Component,官方例程就是一个很好的Function Component,我这边将其简单改写成Class Component。

import * as React from 'react';

import { Checkbox, Stack } from '@fluentui/react';

export class CheckBoxSex extends React.Component {

    // Used to add spacing between checkboxes
    private stackTokens = { childrenGap: 10 };
    
    public render() {
        return (
            <Stack tokens={this.stackTokens}>
                <Checkbox label="female" defaultChecked onChange={this._onChange} />
                <Checkbox label="male" defaultChecked onChange={this._onChange} />
            </Stack>
        );
    }

    private _onChange(ev?: React.FormEvent<HTMLElement | HTMLInputElement>, isChecked?: boolean) {
        console.log(`The option has been changed to ${isChecked}.`);
    }
}

信息显示区域DetailsInfo

这里重新看一下数据:

[
  {"Name": "Tom", "Team": "Tom & Jerry", "Sex": "male"},
  {"Name": "Lei Li", "Team": "English Learning", "Sex": "male"},
  {"Name": "Meimei Han", "Team": "English Learning", "Sex": "female"},
  {"Name": "Jerry", "Team": "Tom & Jerry", "Sex": "male"}
]

一个人有三个属性,TypeScript中可以接口来表示这个对象。我们再在src文件夹下创建一个Model文件夹,添加一个PageModel.ts文件。添加一个接口

export interface PersonInfo {
    Name: string,
    Team: string,
    Sex: string
}

然后可以将刚刚的静态数据先放到组件里面,动态获取的话下章再说。

然后,因为我们希望将所有人按组分类,所以再添加一个接口用于分组

export interface TeamInfo {
    TeamName: string,
    Persons: PersonInfo[]
}

简单写了个Group的方法

import * as React from 'react';

import { PersonInfo, TeamInfo } from '../Model/PageModel';
import { Stack, Text } from '@fluentui/react';

export class DetailsInfo extends React.Component {

    private infoArray: PersonInfo[] = [
        {"Name": "Tom", "Team": "Tom & Jerry", "Sex": "male"},
        {"Name": "Lei Li", "Team": "English Learning", "Sex": "male"},
        {"Name": "Meimei Han", "Team": "English Learning", "Sex": "female"},
        {"Name": "Jerry", "Team": "Tom & Jerry", "Sex": "male"}
    ];

    private getTeamInfoArr(infoArray: PersonInfo[]): TeamInfo[] {
        let teamInfoArray: TeamInfo[] = new Array<TeamInfo>();
        let teamInfoMap: Map<String, PersonInfo[]> = new Map();
        infoArray.forEach((personInfo) => {
            if (!teamInfoMap.has(personInfo.Team)) {
                teamInfoMap.set(personInfo.Team, new Array<PersonInfo>());
            }
            teamInfoMap.get(personInfo.Team)?.push(personInfo);
        });
        // console.log(teamInfoMap);
        teamInfoMap.forEach((personArr, teamName) => {
            teamInfoArray.push({
                TeamName: teamName.toString(), 
                Persons: personArr
            });
        });
        // console.log(teamInfoArray);
        return teamInfoArray;
    }

    public render() {
        let teams = this.getTeamInfoArr(this.infoArray);        
        return (
            <Stack>
                {
                    teams.map((teamInfo) => (
                        <Stack>
                            <Text variant={"xLarge"}>{teamInfo.TeamName}</Text>
                            {
                                teamInfo.Persons.map((personInfo) => (
                                    <Stack>
                                        {personInfo.Name + '-' + personInfo.Sex}
                                    </Stack>
                                ))
                            }
                        </Stack>
                    ))
                }
            </Stack>
        );
    }
}

最后效果

typescript 有bytes类型么_控件_04


执行

npm start
# or
yarn start

最后界面如上图所示。