飞书项目前端开发入门指南

299 阅读2分钟

前文已经对飞书项目插件开发中的一些常见后端场景予以了介绍。在本章节中,我们将主要探讨前端开发中存在的一些问题。并且,我们会通过一个实际的功能,引领你走进插件开发的前端世界。

控件介绍

控件实际上是飞书项目中最为普适的一个插件点位,能够覆盖大部分的交互场景。在控件中,可以自定义 UI 以及交互逻辑,以此来达到我们的理想效果。例如,我们今天要实现的功能 ——Excel 导入功能。

前置准备

  1. 全局安装命令行工具
 npm i -g @lark-project/cli

2. 初始化插件工程

lpm init plugin_demo [plugin-id] [plugin-secret]

3. 启动工程

cd plugin_demo && lpm start

4. 打开飞书项目开心调试

开发过程

可以看到,插件的模板里已经为我们提供了一个控件的 demo。那么,现在我们就着手对其进行修改,从无到有地实现 Excel 导入功能吧。

首先我们需要把控件拖入页面中进行调试。

image.png

image.png(在实例中的展示)

1. 安装依赖项

“xlsx” 能够轻松读取复杂的 Excel 表格,同时也可以生成新的表格。因此,我们安装 “xlsx” 来处理 Excel 数据。

yarn add xlsx

2.处理数据

在控件中选择 Excle 表格并且处理数据

const handleChange = useCallback(({ fileList, currentFile }) => {
    try {
      if (!currentFile) {
        return;
      }
      const render = new FileReader();
      render.readAsBinaryString(currentFile.fileInstance);
      render.onload = e => {
        if (e.target && e.target.result) {
          // 读取文件生成workbook
          const workbook = XLSX.read(e.target.result, { type: 'binary' });
          const { SheetNames } = workbook;
          const worksheet = workbook.Sheets[SheetNames[0]];
          const jsonData: any[] = XLSX.utils.sheet_to_json(worksheet);
          console.log('🚀🌈🌈🌈 ~ handleChange ~ jsonData:', jsonData);
        }
      };
    } catch (error) {
      console.error(error);
    }
}, []);

image.png 可以看到数据解析成功

3. 生成工作项

调用接口创建工作项

const newData = convertData(data);
for (const item of newData) {
  // 注意: 这里调用后端接口, 后端需要调用 openapi 创建工作项
  await axios
    .post('https://xxxxx', {
      work_item_type_key: 'story', // 工作项类型(根据实际情况传递)
      ...item,
    })
    .then(res => {
      Toast.success('创建成功');
    });
}

image.png 接口调用成功后生成对应工作项

完整代码

import React, { useState } from 'react';
import { Button, Toast, Upload } from '@douyinfe/semi-ui';
import { IControlFormItemProps } from '../../../../../constants/type';
import * as XLSX from 'xlsx';
import axios from 'axios';

const DisplayFormItem = (props: IControlFormItemProps) => {
  const [data, setData] = useState<Record<string, any>>([]);

  const handleChange = ({ fileList, currentFile }) => {
    try {
      if (!currentFile) {
        return;
      }
      const render = new FileReader();
      render.readAsBinaryString(currentFile.fileInstance);
      render.onload = e => {
        if (e.target && e.target.result) {
          // 读取文件生成workbook
          const workbook = XLSX.read(e.target.result, { type: 'binary' });
          const { SheetNames } = workbook;
          const worksheet = workbook.Sheets[SheetNames[0]];
          const jsonData: any[] = XLSX.utils.sheet_to_json(worksheet);
          setData(jsonData);
        }
      };
    } catch (error) {
      console.error(error);
    }
  };

  // 转换为后端可用数据
  const convertData = data => {
    try {
      return data.map(item => ({
        name: item.name,
        field_value_pairs: [
          {
            field_key: 'description',
            field_value: item.description,
          },
        ],
      }));
    } catch (error) {
      console.error(error);
    }
    return null;
  };

  const handleCreateWorkItem = async () => {
    const newData = convertData(data);
    for (const item of newData) {
      // 注意: 这里调用后端接口, 后端需要调用 openapi 创建工作项
      await axios
        .post('https://xxxxx', {
          work_item_type_key: 'story', // 工作项类型(根据实际情况传递)
          ...item,
        })
        .then(res => {
          Toast.success('创建成功');
        });
    }
  };

  if (props.mode === 'configure') {
    return '配置页不支持展示';
  }

  return (
    <div>
      <Button
        disabled={data.length === 0}
        onClick={handleCreateWorkItem}
        style={{ marginBottom: 8 }}
      >
        生成工作项
      </Button>
      <Upload
        action=""
        limit={1}
        draggable={true}
        dragMainText={'点击上传文件或拖拽文件到这里'}
        dragSubText="支持任意类型文件"
        uploadTrigger="custom"
        onChange={handleChange}
      />
    </div>
  );
};

export default DisplayFormItem;