• vue自建h5应用,接入企业微信JDK(WECOM-JSSDK),实现跳转添加好友功能


    一、项目场景:

    1、使用vue开发了一套h5页面的项目
    2、这个h5链接是在企业微信里某个地方打开的
    3、打开页面的时候有一个好友列表,点击好友列表某一条复制手机号跳转到企业微信添加好友页面

    二、实现的效果图

    在这里插入图片描述

    博客只允许上传gif图,所以我只能手机进行录屏,然后再拿gif录一次video给大家展示(如果他不动了,你就刷新一下页面)

    三、实现方案

    1、通过 npm 引入(企业微信 WECOM-JSSDK)

    //第一种方式  npm引入
    npm install @wecom/jssdk   (我用的这种)
    //也有第二种方法(别问为啥不用这个,问就是因为我不会)
    <script src="https://wwcdn.weixin.qq.com/node/open/js/wecom-jssdk-1.3.1.js"></script>
    

    2、页面中使用

    import * as ww from '@wecom/jssdk'
    

    3、页面初始化注册企微的jdk(强度来了,非非非常烧脑,需要细看细看细看)

    在看这条之前你需要先明白几点:
    1、想要调用企微的东西,通俗说需要先把你自己的项目让企微进行授权,简单思路就是:
    appid+url----> 换code ---->拿code---->换access_token
    2、(解释第一点)你拿自己的企微id(appid)以及页面地址,去获取企微的code,拿到code后去换取access_token,拿到token后就能进行你自己页面的接口等其他操作了

    created() {
        let appid = "wx7*************";    这是你的企微id
        this.url = location.href.split("#")[0];     获取当前页面的地址,注意你的路由模式,如果路由中携带#,那么就需要截取#之前的 (有问题参考文章后参考资料第三个链接内的第六点)
        this.initCode(appid);   这个方法就是获取企微授权那一套
    },
    methods: {
    	//初始化页面调用方法,如果没有code,就去企微进行授权(企微授权后会返回到这个页面,并且吧code赋到url后边,截取拿地址栏的参数就ok),如果有了code就走自己页面的正常逻辑
    	 async initCode(appid) {
          let code = this.getUrlParam("code");
          if (!code) {
          	//这个地方其实就是死套路,你只需要关注给企微的链接传入appid,url就可以
            const urls = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${this.url}&response_type=code&scope=snsapi_base&state=highSeas#wechat_redirect`;
            window.location.href = urls;
          } else {
          //这块就是企微回调回code后回到这个页面,然后拿code获取access_token就可以进行后续的接口及企微jdk的初始化了
            let postData = {
              code: code,
            };
            qrLogin(postData).then((res) => {
           	 //把token存起来
              sessionStorage.setItem("token", res.access_token);
              this.wxConfig(appid);
            });
          }
        },
         // 获取url参数  (封装的一个获取ulr地址参数的方法)  
        getUrlParam(name) {
          const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
          const result = window.location.search.substr(1).match(reg);
          return result ? decodeURIComponent(result[2]) : null;
        },
        最最最最关键点来了 ---- 关键 ------  ---- 关键 ------  ---- 关键 ------  ---- 关键 ------  ---- 关键 ------
        //初始化注册企微jdk
        wxConfig(appid) {
          let dataInfo = {
            url: this.url,
            appType: 1,
          };
          //调用企微的方法,用url去拿签名等信息  
          //我们请求的是这个接口  “ticket/getAgentTicket”
          //https://developer.work.weixin.qq.com/document/24364#%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%20jsapi_ticket
          //企微的链接,生成签名算法,需要后端配合你一块查看,如果这个签名生成不正确就会报错,比如后续图一,
          getAgentTicket(dataInfo).then((res) => {
            this.dataVal = res.data.data;
            let timestamp = this.dataVal.timestamp;
            let nonceStr = this.dataVal.nonceStr;
            let signature = this.dataVal.signature;
            ww.register({
              corpId: appid, // 必填,当前用户企业所属企业ID
              agentId: "1000XXX",      //找后端宝贝要
              jsApiList: ["navigateToAddCustomer", "scanQRCode", "openUserProfile"], // 必填,需要使用的JSAPI列表  
              getConfigSignature, // 必填,根据url生成企业签名的回调函数
              getAgentConfigSignature,    // 必填,根据url生成应用签名的回调函数
              onConfigSuccess: (result) => {
                // alert(result,"success");
              },
              onConfigFail: (result) => {
                alert(JSON.stringify(result), "failed");
              },
            });
            function getConfigSignature(url) {
              // 根据 url 生成企业签名
              // 生成方法参考 https://developer.work.weixin.qq.com/document/path/90539
              return { timestamp, nonceStr, signature };
            }
            async function getAgentConfigSignature(url) {
              // 根据 url 生成应用签名,生成方法同上,但需要使用应用的 jsapi_ticket
              return { timestamp, nonceStr, signature };
            }
          });
          //截止到这块初始化企微jdk就结束了,接下来就可以直接做你想要的操作了,比如跳转到企微的添加好友,比如调用企微的扫一扫等
        },
        //接下来就用按钮绑定一个事件,调用你想用的方法就行了
        setGoto(text) {
            ww.navigateToAddCustomer({
              success(result) {
                // alert(JSON.stringify(result), "1111");
                // 成功回调,result.errMsg 固定格式为“方法名:ok”
              },
              fail(result) {
                alert(JSON.stringify(result), "32222");
                // 失败回调,通过 result.errMsg 查看失败详情
              },
              complete(result) {
                // alert(JSON.stringify(result), "333333");
                // 完成回调,无论调用成功还是失败,都会回调该方法
              },   
          });
        },
    }
    

    四、注意点

    4.1、无效签名

    在这里插入图片描述

    这个代码40093问题是很常见的,当时一直在翻企微的社区文章找错误解决办法,结果改了一圈没有一个类似的,然后又重新返回去按照文章步骤去一步一步排查,终于发现了问题点在哪

    在这里插入图片描述

    解决点1:我用的是npm引入的方法,并且是h5自建应用,所以应该用这种方法去初始化,而我用的是上边“企业身份与权限”,没细看自己的需求直接文章都不翻去用,导致卡了很久
    解决点2:后端直接扔个我一个获取签名的接口getAppTicket 说只有这么一个接口绝对没问题,然后又去拿签名验证工具重复的看也没问题,死活非说前端报错,这可一顿好找啊,后来我就一直纠结那个签名算法生成的文章,逼着后端宝贝一块看真的没问题嘛?最终发现了。又给了我一个getAgentTicket接口,说换这个试试,然后一举成功。
    getAgentTicket 获取应用的jsapi_ticket
    getAppTicket 获取企业的jsapi_ticket

    4.2 没有权限

    在这里插入图片描述

    1、调到这块我信誓旦旦觉得没问题了,所以就去手机端尝试,结果又给我报这个错没有权限。
    2、我以为是哪块又配置的不对,初始化的时候在jsApiList也配置了自己想要的api了啊。然后我就去调用的扫一扫的功能ww.scanQRCode({needResult: true,scanType: [‘qrCode’]}) 放到手机里直接使用,可以出来扫一扫界面,那就找到了初始化这块肯定是成功了,那就是这个跳转好友这块有问题,然后又去翻文章。

    在这里插入图片描述
    在这里插入图片描述

    1、后台配置一下添加好友权限即可

    至此全部功能已经实现,总结来说按照官方文档步步排查走踩坑少,自己想做什么都不明确,光靠一个报错去搜索解答会浪费很多时间。多看文档,少瞎胡搜报错,少走弯路
    还有一个点,不管是初始化jdk还是调用后续的api方法,企微都有Success,跟Fail的回调,可以快速的帮助你找到报错原因。

    4.3这个常见报错文档很有用,多看

    在这里插入图片描述

    参考资料

    主要的参考资料链接,其实都是企微的官方文档,主要注意的点就差不多这几个
    https://developer.work.weixin.qq.com/document/path/98132 (第一个肯定是企业微信的开发文档了,项目怎么接入企微的jdk)
    https://developer.work.weixin.qq.com/document/24364#%E8%8E%B7%E5%8F%96%E5%BA%94%E7%94%A8%20jsapi_ticket (这个还是属于企微的文档,但也是最关键的一步,怎么生成签名——需要后端同事一块查看)
    https://developer.work.weixin.qq.com/document/path/90542 (依旧企微的文档,常见错误问题排查,比如最主要的签名错误)

    全部代码(如果你功能跟我一样或者类似呢,盲猜替换一下appid跟agentId就可以直接使用)

    <template>
      <van-tabs sticky v-model="active" @click-tab="onClickTab">
        <van-tab name="first" :title="'待添加(' + total + ')'">
          <van-list
            v-if="active == 'first'"
            v-model:loading="loading"
            :finished="finished"
            finished-text="没有更多了"
            :immediate-check="false"
            @load="getData"
            :offset="20"
            class="contentList"
          >
            <van-cell>
              <template v-for="(unit, key) in list" :key="key">
                <div class="content">
                  <div>{{ unit.phone }}</div>
                  <div>
                    {{ unit.customerName }}
                  </div>
                  <div style="display: flex">
                    <van-button
                      plain
                      size="small"
                      hairline
                      type="primary"
                      @click="copyFn($event, unit.phone)"
                      >复制并添加</van-button
                    >
                  </div>
                </div>
              </template>
            </van-cell>
          </van-list>
        </van-tab>
        <van-tab name="second" title="待通过"></van-tab>
        <van-tab name="three" title="已添加"></van-tab>
      </van-tabs>
    </template>
       
      <script>
    import {
      getTypeList,
      setState,
      getAgentTicket,
      wcRedirect,
      qrLogin,
    } from "../api/highSeas";
    import { showNotify, closeNotify } from "vant";
    import ClipboardJS from "clipboard";
    import * as ww from "@wecom/jssdk";
    export default {
      data() {
        return {
          active: "first",
          list: [],
          loading: false,
          finished: false,
          total: 0,
          query: {
            pageSize: 20,
            pageNum: 1,
            addState: 0, // 0:待添加;1:已添加;3:待通过
          },
        };
      },
      created() {
        let appid = "wxXXXXXXXXXXXXXXXX";
        this.url = location.href.split("#")[0];
        this.initCode(appid);
      },
      methods: {
        onClickTab(info) {
          this.active = info.name;
          if (info.name == "first") {
            this.query.pageNum = 1;
            this.list = [];
            this.loading = false;
            this.finished = false;
            getTypeList(this.query).then((res) => {
              this.total = res.data.total;
              this.list.push(...res.data.rows);
            });
            this.query.pageNum = 2;
          }
        },
        async initCode(appid) {
          let code = this.getUrlParam("code");
          if (!code) {
            const urls = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${this.url}&response_type=code&scope=snsapi_base&state=highSeas#wechat_redirect`;
            window.location.href = urls;
          } else {
            let postData = {
              code: code,
            };
            qrLogin(postData).then((res) => {
              this.initQueryVal = true;
              sessionStorage.setItem("token", res.access_token);
              this.init();
              this.wxConfig(appid);
            });
          }
        },
        // 获取url参数
        getUrlParam(name) {
          const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
          const result = window.location.search.substr(1).match(reg);
          return result ? decodeURIComponent(result[2]) : null;
        },
        wxConfig(appid) {
          let dataInfo = {
            url: this.url,
            appType: 1,
          };
          getAgentTicket(dataInfo).then((res) => {
            this.dataVal = res.data.data;
            let timestamp = this.dataVal.timestamp;
            let nonceStr = this.dataVal.nonceStr;
            let signature = this.dataVal.signature;
            ww.register({
              corpId: appid, // 必填,当前用户企业所属企业ID
              agentId: "10XXXXXX",
              jsApiList: ["navigateToAddCustomer", "scanQRCode", "openUserProfile"], // 必填,需要使用的JSAPI列表
              getConfigSignature, // 必填,根据url生成企业签名的回调函数
              getAgentConfigSignature,
              onConfigSuccess: (result) => {
                // alert(result,"success");
              },
              onConfigFail: (result) => {
                alert(JSON.stringify(result), "failed");
              },
            });
            function getConfigSignature(url) {
              // 根据 url 生成企业签名
              // alert(this.dataVal.nonceStr, "this.dataVal.nonceStr");
              // alert(
              //   this.dataVal.signature,
              //   "this.dataVal.signaturesignaturesignaturesignaturesignature"
              // );
              // 生成方法参考 https://developer.work.weixin.qq.com/document/path/90539
              return { timestamp, nonceStr, signature };
            }
            async function getAgentConfigSignature(url) {
              // 根据 url 生成应用签名,生成方法同上,但需要使用应用的 jsapi_ticket
              return { timestamp, nonceStr, signature };
            }
          });
        },
        init() {
          getTypeList(this.query).then((res) => {
            this.total = res.data.total;
            this.list.push(...res.data.rows);
          });
          this.query.pageNum = 2;
        },
        getData() {
          getTypeList(this.query).then((res) => {
            let dataInfo = res.data.rows;
            this.loading = false;
            this.query.pageNum += 1;
            this.total = res.data.total;
            this.list.push(...dataInfo);
            if (this.list.length >= Number(this.total)) {
              this.finished = true;
            } else {
              this.finished = false;
            }
          });
        },
        setGoto(text) {
          setState({ phone: text }).then((res) => {
            ww.navigateToAddCustomer({
              success(result) {
                // alert(JSON.stringify(result), "1111");
                // 成功回调,result.errMsg 固定格式为“方法名:ok”
              },
              fail(result) {
                alert(JSON.stringify(result), "32222");
                // 失败回调,通过 result.errMsg 查看失败详情
              },
              complete(result) {
                // alert(JSON.stringify(result), "333333");
                // 完成回调,无论调用成功还是失败,都会回调该方法
              },
            });
            this.list = [];
            this.query.pageNum = 1;
            this.finished = false;
            this.init();
          });
        },
        copyFn(e, text) {
          const clipboard = new ClipboardJS(e.target, { text: () => text });
          clipboard.on("success", (e) => {
            this.setGoto(text);
            showNotify({ type: "success", message: "复制成功" });
            // 释放内存
            clipboard.off("error");
            clipboard.off("success");
            clipboard.destroy();
          });
          clipboard.on("error", (e) => {
            // 不支持复制
            // this.$toast({
            //   type: "fail",
            //   message: "该浏览器不支持自动复制",
            //   icon: "none",
            // });
            showNotify({ type: "warning", message: "该浏览器不支持自动复制" });
            // 释放内存
            clipboard.off("error");
            clipboard.off("success");
            clipboard.destroy();
          });
          clipboard.onClick(e);
        },
      },
    };
    </script>
    <style scoped lang='scss'>
    .content {
      font-size: 15px;
      font-weight: 500;
      display: flex;
      justify-content: space-between;
      align-items: center;
      line-height: 60px;
    }
    .contentList {
      // overflow-y: scroll;
      // height: calc(100vh - 60px); //
    }
    
    ::v-deep .van-button--info {
      border: none;
    }
    </style>
    
  • 相关阅读:
    分布式服务与分布式框架
    第一季:3类和实例初始化【Java面试题】
    安卓毕业设计app项目源码移动端的医生寻访平台
    【JDK 8-集合框架进阶】6.1 parallelStream 并行流
    Vue 绑定style和class
    wow-string-list文件说明
    iOS App Store上传项目报错 缺少隐私政策网址(URL)解决方法
    Google Earth Engine(GEE)——checkbox的使用
    嵌入式基础-电路
    深入解析Node.js的process.cpuUsage():监控与优化CPU使用
  • 原文地址:https://blog.csdn.net/wangjiecsdn/article/details/130399509