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


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

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

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

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

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

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

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

    5. 使用该组件,最开始的一件事就是要完成一圈授权流程,在这里将会用到的授权的参数记录如下:

    1. private static final String APP_ID = "";
    2. private static final String APP_SECRET = "";
    3. /** 用appId和appSecret获取tenant_access_token(租户授权,目前用不到,暂时留存) */
    4. private static final String TENANT_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
    5. /** 获取ticket参数的api */
    6. private static final String JSAPI_TICKET_URI = "https://open.feishu.cn/open-apis/jssdk/ticket/get";
    7. /** 加签sha1的一个随机参数 */
    8. private static final String NONCE_STR = "abc123def456";
    9. /** 用appId和appSecret获取app_access_token(应用级别的授权(只允许查看,不允许编辑,分享等功能权限)) */
    10. private static final String APP_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";
    11. /** 用户访问令牌(用户级别授权,和用户权限挂钩,唯有这种授权在用户拥有权限的情况下,可以实现在线编辑文档,目前暂不做) */
    12. private static final String USER_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/authen/v1/access_token";
    13. /** 用户信息接口路径 */
    14. private static final String USER_INFO_URI = "https://open.feishu.cn/open-apis/authen/v1/user_info";
    15. 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请求封装,自己引入或者改一下):

    1. package com.ruoyi.biz.controller;
    2. import java.util.ArrayList;
    3. import java.util.HashMap;
    4. import java.util.List;
    5. import java.util.Map;
    6. import org.springframework.web.bind.annotation.GetMapping;
    7. import org.springframework.web.bind.annotation.RequestMapping;
    8. import org.springframework.web.bind.annotation.RestController;
    9. import com.alibaba.fastjson.JSONObject;
    10. import com.ruoyi.biz.vo.FeishuWebComponentAuthVo;
    11. import com.ruoyi.common.core.controller.BaseController;
    12. import com.ruoyi.common.core.domain.AjaxResult;
    13. import cn.hutool.http.HttpRequest;
    14. import cn.hutool.http.HttpUtil;
    15. import lombok.extern.slf4j.Slf4j;
    16. /**
    17. * @author zhaoyuyang
    18. */
    19. @RestController
    20. @RequestMapping("/biz/help")
    21. @Slf4j
    22. public class HelpController extends BaseController
    23. {
    24. private static final String APP_ID = "";
    25. private static final String APP_SECRET = "";
    26. /** 用appId和appSecret获取tenant_access_token(租户授权,目前用不到,暂时留存) */
    27. private static final String TENANT_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal";
    28. /** 获取ticket参数的api */
    29. private static final String JSAPI_TICKET_URI = "https://open.feishu.cn/open-apis/jssdk/ticket/get";
    30. /** 加签sha1的一个随机参数 */
    31. private static final String NONCE_STR = "abc123def456";
    32. /** 用appId和appSecret获取app_access_token(应用级别的授权(只允许查看,不允许编辑,分享等功能权限)) */
    33. private static final String APP_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";
    34. /** 用户访问令牌(用户级别授权,和用户权限挂钩,唯有这种授权在用户拥有权限的情况下,可以实现在线编辑文档,目前暂不做) */
    35. private static final String USER_ACCESS_TOKEN_URI = "https://open.feishu.cn/open-apis/authen/v1/access_token";
    36. /** 用户信息接口路径 */
    37. private static final String USER_INFO_URI = "https://open.feishu.cn/open-apis/authen/v1/user_info";
    38. private static final String DOCUMENT_URL = "替换你自己的文档全路径";
    39. /**
    40. * 获取签名数据
    41. */
    42. @GetMapping("/getSignData")
    43. public AjaxResult getSignData()
    44. {
    45. // 1. 通过app_id和app_secret获取app_access_token
    46. Map paramMap = new HashMap<>();
    47. paramMap.put("app_id", HelpController.APP_ID);
    48. paramMap.put("app_secret", HelpController.APP_SECRET);
    49. String returnStr = HttpUtil.post(HelpController.APP_ACCESS_TOKEN_URI,
    50. paramMap);
    51. JSONObject jsonObject = JSONObject.parseObject(returnStr);
    52. String appAccessToken = jsonObject.getString("app_access_token");
    53. // 2.通过tenantAccessToken获取ticket
    54. String ticketResponseStr = HttpRequest
    55. .post(HelpController.JSAPI_TICKET_URI)
    56. .header("Authorization", "Bearer " + appAccessToken).execute()
    57. .body();
    58. String ticket = JSONObject.parseObject(ticketResponseStr)
    59. .getJSONObject("data").getString("ticket");
    60. // 3.按照要求拼接Signature,然后使用sha1进行加密
    61. StringBuilder signatureBuilder = new StringBuilder();
    62. Long timeStamp = System.currentTimeMillis();
    63. signatureBuilder.append("jsapi_ticket=").append(ticket)
    64. .append("&noncestr=").append(HelpController.NONCE_STR)
    65. .append("×tamp=").append(timeStamp).append("&url=")
    66. .append(HelpController.DOCUMENT_URL);
    67. String signature = cn.hutool.crypto.SecureUtil
    68. .sha1(signatureBuilder.toString());
    69. // 4.返回数据,组装数据
    70. FeishuWebComponentAuthVo returnVo = new FeishuWebComponentAuthVo();
    71. List list = new ArrayList<>();
    72. list.add("DocsComponent");
    73. // 如果为app_access_token模式进行授权,则不输入openId参数
    74. returnVo.setOpenId("").setSignature(signature)
    75. .setAppId(HelpController.APP_ID)
    76. .setTimestamp(timeStamp.toString())
    77. .setNonceStr(HelpController.NONCE_STR)
    78. .setUrl(HelpController.DOCUMENT_URL).setJsApiList(list)
    79. .setLocale("zh-CN");
    80. return AjaxResult.success(returnVo);
    81. }
    82. }

    8.前端代码:

    1. <template>
    2. <div class="app-container">
    3. <div id="feishu-page">div>
    4. div>
    5. template>
    6. <script>
    7. import { getSignData } from '@/api/biz/help'
    8. export default {
    9. data() {
    10. return {}
    11. },
    12. created() {
    13. this.$modal.loading('正在加载文档')
    14. },
    15. mounted() {
    16. const script = document.createElement('script')
    17. script.src =
    18. 'https://lf1-cdn-tos.bytegoofy.com/goofy/locl/lark/external_js_sdk/h5-js-sdk-1.1.3.js'
    19. script.async = true
    20. script.onload = () => {
    21. // 执行一些操作,例如初始化该库或者调用一些方法
    22. this.authPage()
    23. }
    24. document.head.appendChild(script)
    25. },
    26. methods: {
    27. authPage() {
    28. getSignData().then((response) => {
    29. let openId = response.data.openId
    30. let signature = response.data.signature
    31. let appId = response.data.appId
    32. let timestamp = response.data.timestamp
    33. let nonceStr = response.data.nonceStr
    34. let url = response.data.url
    35. let jsApiList = response.data.jsApiList
    36. let locale = response.data.locale
    37. let that = this
    38. window.webComponent
    39. .config({
    40. openId, // 当前登录用户的open id,要确保与生成 signature 使用的 user_access_token 相对应,使用 app_access_token 时此项不填。注意:仅云文档组件可使用app_access_token
    41. signature, // 签名
    42. appId, // 应用 appId
    43. timestamp, // 时间戳(毫秒)
    44. nonceStr, // 随机字符串
    45. url, // 第3步参与加密计算的url,同时也是最终需要访问的url
    46. jsApiList, // 指定要使用的组件列表,请根据对应组件的开发文档填写。如云文档组件,填写['DocsComponent']
    47. locale, // 指定组件的国际化语言:en-US-英文、zh-CN-中文、ja-JP-日文
    48. })
    49. .then((res) => {
    50. // 动态渲染,返回组件实例。
    51. let myComponent = window.webComponent.render(
    52. 'DocsComponent',
    53. {
    54. //组件参数
    55. src: url,
    56. minHeight: '500px',
    57. height: '1100px', //该参数控制了页面的最大展开高度,不允许为Auto,也不能不写,会导致锚点定位,文本搜索失效
    58. width: '100%',
    59. },
    60. document.querySelector('#feishu-page'), // 将组件挂在到哪个元素上
    61. )
    62. // 通过setFeatureConfig方法修改组件的配置属性
    63. myComponent.config.setFeatureConfig({
    64. //文档头部菜单栏
    65. HEADER: {
    66. enable: true, // 隐藏头部
    67. },
    68. //点赞
    69. LIKE: {
    70. enable: true, // 隐藏点赞
    71. },
    72. //图片
    73. IMAGE: {
    74. maxWidth: 500, // 图片最大宽度
    75. },
    76. //分享
    77. SHARE: {
    78. enable: false, // 是否显示分享按钮
    79. visibleConfig: {
    80. // 区域的显隐配置
    81. invite: false, //邀请区域
    82. shareLink: false, //分享链接区域
    83. shareMethod: false, //分享方式区域
    84. },
    85. },
    86. //模态窗
    87. MODAL: {
    88. innerMask: true, // 有模态窗时, 组件内是否有遮罩
    89. outerMask: {
    90. enable: true, // 有模态窗时, 组件外是否有遮罩
    91. zIndex: 1000, // 遮罩层的z-index
    92. },
    93. offset: {
    94. // 模态窗的偏移量
    95. x: 0,
    96. y: 0,
    97. },
    98. },
    99. //文档内容
    100. CONTENT: {
    101. readonly: false, // 是否只读
    102. padding: [0, 0, 0, 0], // 文档内边距,例:[10, 0, 10, 0] 为上下增加 10 px 边距
    103. maxWidth: 0, //内部最大宽度
    104. titleVisible: true, //标题是否可见
    105. unscrollable: false, //云文档组件是都允许滚动
    106. },
    107. //评论
    108. COMMENT: {
    109. partial: {
    110. //局部评论
    111. enable: false, //是否启用局部评论
    112. open: false, //是否展开局部评论侧栏
    113. },
    114. global: {
    115. //全局评论
    116. enable: false, //是否启用全局评论
    117. },
    118. },
    119. //侧边栏
    120. SIDEBAR: {
    121. borderSide: [false, false, false, false], //侧边栏边框显隐,例:[true, true, false, false] 上、右侧有边框
    122. },
    123. //目录
    124. DIRECTORY: {
    125. enable: true, //是否显示目录
    126. pin: true, //是否固定目录
    127. },
    128. //文档头部右侧更多菜单
    129. MORE_MENU: {
    130. enable: true, //是否显示更多菜单
    131. items: {
    132. //菜单子项
    133. findAndReplaceEnable: true, //是否显示查找替换
    134. makeCopyEnable: false, //是否显示创建副本
    135. applyEditPermissionEnable: false, //是否显示申请编辑权限入口
    136. exportEnable: false, //是否显示导出
    137. documentInfoEnable: false, //是否显示文档详情
    138. editHistoryEnable: false, //是否显示编辑历史'
    139. commentHistoryEnable: false, //是否显示评论历史
    140. translateEnable: false, //是否显示翻译
    141. printEnable: false, //是否显示打印
    142. deleteEnable: false, //是否显示删除
    143. },
    144. },
    145. //右下角全屏按钮
    146. FULLSCREEN: {
    147. enable: true, //是否显示按钮
    148. },
    149. //文档头部右侧协作者列表
    150. COLLABORATOR: {
    151. enable: false, //是否显示协作者列表
    152. },
    153. })
    154. myComponent.event.onMountSuccess(function () {
    155. that.$modal.closeLoading()
    156. })
    157. })
    158. })
    159. },
    160. },
    161. }
    162. script>

  • 相关阅读:
    openssl生成key和pem文件
    【深度学习】实验08 TensorBoard案例
    C#利用Refit实现JWT自动续期
    计网第五章(运输层)(四)(TCP的流量控制)
    深入解析Java集合框架:接口、实现类及方法全面对比
    C语言日记 36 类的组合
    解决前端可以进入首页,菜单导航无法加载
    第二天表格练习
    计算机竞赛 深度学习交通车辆流量分析 - 目标检测与跟踪 - python opencv
    主席树的板子
  • 原文地址:https://blog.csdn.net/Zachariahs/article/details/133132610