vue页面嵌入飞书网页组件,用于在类似ERP,OA等系统中展示在线文档

先展示最终效果(就是在vue页面中,内嵌了一块ifream页面):

  1. 注册进入飞书开放平台,地址为: 飞书开放平台

2.进入开放平台后,选择--创建企业自建应用--创建网页应用,然后在主页面记住该应用的appId和appSecret参数,后面要用

3.然后注意一点的是,因为后面的授权等逻辑我们一般用我们自己的文档做测试,但是自己的文档也属于企业,所以需要将平台应用正式上线,这个需要企业管理员审核,提前说明一下,还需要开通部分接口和数据权限,也是需要审核的.建议最好弄个管理员账号过来开一下

4.在开放平台--开发者后台这里,点击左侧菜单栏的[添加应用能力]页签,然后在右侧菜单选择网页组件

然后在弹出框中点击[如何开发]进入到官方文档中

进入文档页面后,切换左侧的菜单到网页组件中,使用这个组件:

  1. 使用该组件,最开始的一件事就是要完成一圈授权流程,在这里将会用到的授权的参数记录如下:
java 复制代码
private static final String APP_ID                  = "";

private static final String APP_SECRET              = "";

/** 用appId和appSecret获取tenant_access_token(租户授权,目前用不到,暂时留存) */
private static final String TENANT_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";

/** 获取ticket参数的api */
private static final String JSAPI_TICKET_URI        = "https://open.feishu.cn/open-apis/jssdk/ticket/get";

/** 加签sha1的一个随机参数 */
private static final String NONCE_STR               = "abc123def456";

/** 用appId和appSecret获取app_access_token(应用级别的授权(只允许查看,不允许编辑,分享等功能权限)) */
private static final String APP_ACCESS_TOKEN_URI    = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";

/** 用户访问令牌(用户级别授权,和用户权限挂钩,唯有这种授权在用户拥有权限的情况下,可以实现在线编辑文档,目前暂不做) */
private static final String USER_ACCESS_TOKEN_URI   = "https://open.feishu.cn/open-apis/authen/v1/access_token";

/** 用户信息接口路径 */
private static final String USER_INFO_URI           = "https://open.feishu.cn/open-apis/authen/v1/user_info";

private static final String DOCUMENT_URL            = "https://xxxxxxx.feishu.cn/docx/xxxxxxxxxxxxxx";

需要注意的两个点是:

(1). 如果你使用app_access_token去进行后续的验证,那么默认无法获得在线文档编辑的操作,只允许操作,只有当使用user_access_token权限认证,才可以根据用户的权限进行后续的编辑等操作

(2). 请注意DOCUMENT_URL这个参数,这个组件貌似目前只能用/docs下的文档进行渲染,而不可以用我们知识库下的wiki组件,也就是说,你打开的文档如果是wiki的,那么需要将他另存到共享空间中,就会自动变成docs.才可以用

6.授权参数明确后,还有一件事,就是需要开通对应权限,开通对应的权限后还需要上架最新的应用

(1):TENANT_ACCESS_TOKEN_URI不需要开通权限

(2):APP_ACCESS_TOKEN_URI: 不需要开通权限

(3):USER_ACCESS_TOKEN_URI:需要权限如下

7.授权相关后台代码(用了Hutool的Http请求封装,自己引入或者改一下):

java 复制代码
package com.ruoyi.biz.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.biz.vo.FeishuWebComponentAuthVo;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zhaoyuyang
 */
@RestController
@RequestMapping("/biz/help")
@Slf4j
public class HelpController extends BaseController
{
    private static final String APP_ID                  = "";

    private static final String APP_SECRET              = "";

    /** 用appId和appSecret获取tenant_access_token(租户授权,目前用不到,暂时留存) */
    private static final String TENANT_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";

    /** 获取ticket参数的api */
    private static final String JSAPI_TICKET_URI        = "https://open.feishu.cn/open-apis/jssdk/ticket/get";

    /** 加签sha1的一个随机参数 */
    private static final String NONCE_STR               = "abc123def456";

    /** 用appId和appSecret获取app_access_token(应用级别的授权(只允许查看,不允许编辑,分享等功能权限)) */
    private static final String APP_ACCESS_TOKEN_URI    = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";

    /** 用户访问令牌(用户级别授权,和用户权限挂钩,唯有这种授权在用户拥有权限的情况下,可以实现在线编辑文档,目前暂不做) */
    private static final String USER_ACCESS_TOKEN_URI   = "https://open.feishu.cn/open-apis/authen/v1/access_token";

    /** 用户信息接口路径 */
    private static final String USER_INFO_URI           = "https://open.feishu.cn/open-apis/authen/v1/user_info";

    private static final String DOCUMENT_URL            = "替换你自己的文档全路径";

    /**
     * 获取签名数据
     */
    @GetMapping("/getSignData")
    public AjaxResult getSignData()
    {
        // 1. 通过app_id和app_secret获取app_access_token
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("app_id", HelpController.APP_ID);
        paramMap.put("app_secret", HelpController.APP_SECRET);
        String returnStr = HttpUtil.post(HelpController.APP_ACCESS_TOKEN_URI,
                paramMap);
        JSONObject jsonObject = JSONObject.parseObject(returnStr);
        String appAccessToken = jsonObject.getString("app_access_token");
        // 2.通过tenantAccessToken获取ticket
        String ticketResponseStr = HttpRequest
                .post(HelpController.JSAPI_TICKET_URI)
                .header("Authorization", "Bearer " + appAccessToken).execute()
                .body();
        String ticket = JSONObject.parseObject(ticketResponseStr)
                .getJSONObject("data").getString("ticket");
        // 3.按照要求拼接Signature,然后使用sha1进行加密
        StringBuilder signatureBuilder = new StringBuilder();
        Long timeStamp = System.currentTimeMillis();
        signatureBuilder.append("jsapi_ticket=").append(ticket)
                .append("&noncestr=").append(HelpController.NONCE_STR)
                .append("&timestamp=").append(timeStamp).append("&url=")
                .append(HelpController.DOCUMENT_URL);
        String signature = cn.hutool.crypto.SecureUtil
                .sha1(signatureBuilder.toString());
        // 4.返回数据,组装数据
        FeishuWebComponentAuthVo returnVo = new FeishuWebComponentAuthVo();
        List<String> list = new ArrayList<>();
        list.add("DocsComponent");
        // 如果为app_access_token模式进行授权,则不输入openId参数
        returnVo.setOpenId("").setSignature(signature)
                .setAppId(HelpController.APP_ID)
                .setTimestamp(timeStamp.toString())
                .setNonceStr(HelpController.NONCE_STR)
                .setUrl(HelpController.DOCUMENT_URL).setJsApiList(list)
                .setLocale("zh-CN");
        return AjaxResult.success(returnVo);
    }
}

8.前端代码:

html 复制代码
<template>
  <div class="app-container">
    <div id="feishu-page"></div>
  </div>
</template>
<script>
import { getSignData } from '@/api/biz/help'
export default {
  data() {
    return {}
  },
  created() {
    this.$modal.loading('正在加载文档')
  },
  mounted() {
    const script = document.createElement('script')
    script.src =
      'https://lf1-cdn-tos.bytegoofy.com/goofy/locl/lark/external_js_sdk/h5-js-sdk-1.1.3.js'
    script.async = true
    script.onload = () => {
      // 执行一些操作,例如初始化该库或者调用一些方法
      this.authPage()
    }
    document.head.appendChild(script)
  },
  methods: {
    authPage() {
      getSignData().then((response) => {
        let openId = response.data.openId
        let signature = response.data.signature
        let appId = response.data.appId
        let timestamp = response.data.timestamp
        let nonceStr = response.data.nonceStr
        let url = response.data.url
        let jsApiList = response.data.jsApiList
        let locale = response.data.locale
        let that = this
        window.webComponent
          .config({
            openId, // 当前登录用户的open id,要确保与生成 signature 使用的 user_access_token 相对应,使用 app_access_token 时此项不填。注意:仅云文档组件可使用app_access_token
            signature, // 签名
            appId, // 应用 appId
            timestamp, // 时间戳(毫秒)
            nonceStr, // 随机字符串
            url, // 第3步参与加密计算的url,同时也是最终需要访问的url
            jsApiList, // 指定要使用的组件列表,请根据对应组件的开发文档填写。如云文档组件,填写['DocsComponent']
            locale, // 指定组件的国际化语言:en-US-英文、zh-CN-中文、ja-JP-日文
          })
          .then((res) => {
            // 动态渲染,返回组件实例。
            let myComponent = window.webComponent.render(
              'DocsComponent',
              {
                //组件参数
                src: url,
                minHeight: '500px',
                height: '1100px', //该参数控制了页面的最大展开高度,不允许为Auto,也不能不写,会导致锚点定位,文本搜索失效
                width: '100%',
              },
              document.querySelector('#feishu-page'), // 将组件挂在到哪个元素上
            )
            // 通过setFeatureConfig方法修改组件的配置属性
            myComponent.config.setFeatureConfig({
              //文档头部菜单栏
              HEADER: {
                enable: true, // 隐藏头部
              },
              //点赞
              LIKE: {
                enable: true, // 隐藏点赞
              },
              //图片
              IMAGE: {
                maxWidth: 500, // 图片最大宽度
              },
              //分享
              SHARE: {
                enable: false, // 是否显示分享按钮
                visibleConfig: {
                  // 区域的显隐配置
                  invite: false, //邀请区域
                  shareLink: false, //分享链接区域
                  shareMethod: false, //分享方式区域
                },
              },
              //模态窗
              MODAL: {
                innerMask: true, // 有模态窗时, 组件内是否有遮罩
                outerMask: {
                  enable: true, // 有模态窗时, 组件外是否有遮罩
                  zIndex: 1000, // 遮罩层的z-index
                },
                offset: {
                  // 模态窗的偏移量
                  x: 0,
                  y: 0,
                },
              },
              //文档内容
              CONTENT: {
                readonly: false, // 是否只读
                padding: [0, 0, 0, 0], // 文档内边距,例:[10, 0, 10, 0] 为上下增加 10 px 边距
                maxWidth: 0, //内部最大宽度
                titleVisible: true, //标题是否可见
                unscrollable: false, //云文档组件是都允许滚动
              },
              //评论
              COMMENT: {
                partial: {
                  //局部评论
                  enable: false, //是否启用局部评论
                  open: false, //是否展开局部评论侧栏
                },
                global: {
                  //全局评论
                  enable: false, //是否启用全局评论
                },
              },
              //侧边栏
              SIDEBAR: {
                borderSide: [false, false, false, false], //侧边栏边框显隐,例:[true, true, false, false] 上、右侧有边框
              },
              //目录
              DIRECTORY: {
                enable: true, //是否显示目录
                pin: true, //是否固定目录
              },
              //文档头部右侧更多菜单
              MORE_MENU: {
                enable: true, //是否显示更多菜单
                items: {
                  //菜单子项
                  findAndReplaceEnable: true, //是否显示查找替换
                  makeCopyEnable: false, //是否显示创建副本
                  applyEditPermissionEnable: false, //是否显示申请编辑权限入口
                  exportEnable: false, //是否显示导出
                  documentInfoEnable: false, //是否显示文档详情
                  editHistoryEnable: false, //是否显示编辑历史'
                  commentHistoryEnable: false, //是否显示评论历史
                  translateEnable: false, //是否显示翻译
                  printEnable: false, //是否显示打印
                  deleteEnable: false, //是否显示删除
                },
              },
              //右下角全屏按钮
              FULLSCREEN: {
                enable: true, //是否显示按钮
              },
              //文档头部右侧协作者列表
              COLLABORATOR: {
                enable: false, //是否显示协作者列表
              },
            })
            myComponent.event.onMountSuccess(function () {
              that.$modal.closeLoading()
            })
          })
      })
    },
  },
}
</script>
相关推荐
星空椰6 小时前
Python 使用飞书 API 获取部门直属用户列表(递归获取所有部门 + 导出 Excel)
python·飞书
gf132111112 小时前
飞书长连接_事件订阅(接收消息,审批任务状态变更)
开发语言·python·飞书
组合缺一14 小时前
OpenClaw vs SolonCode:绑定飞书与钉钉,到底谁更简单?
ai·钉钉·飞书·ai编程·数字员工·openclaw·soloncode
野指针YZZ15 小时前
飞书MCP配置cc
飞书
带刺的坐椅1 天前
OpenClaw vs SolonCode:绑定飞书与钉钉,到底谁更简单?
钉钉·飞书·openclaw·soloncode
广州灵眸科技有限公司2 天前
瑞芯微(EASY EAI)RV1126B openclaw部署接入飞书
linux·网络·人工智能·算法·yolo·飞书
YoungHong19923 天前
服务器部署Hermes教程 —— 从零搭建你的私人 AI 助手
飞书·claude·云服务器·deepseek·爱马仕·hermess
a752066283 天前
AI 智能体 OpenClaw 飞书插件安装配置 全程命令行实操
人工智能·飞书·openclaw部署·ai 办公自动化
星空椰3 天前
Python 使用飞书 API 获取子部门列表接口信息
python·pandas·飞书
李高杰9963 天前
Claudecode如何接入飞书呢?
飞书