最近在写web项目的过程中接触到公众号开发,在这码一码整个开发的流程
一、配置微信公众号测试号
微信测试号平台链接 mp.weixin.qq.com
扫描登录进入,获取测试号信息:appID与appsecret,这两个很重要!!页面下方的二维码就是我们本次项目中使用到的测试号,可以自己扫码关注一下
如下是官方的开发文档,感兴趣的可以看看 微信公众平台开发概述 | 微信开放文档 (qq.com)
在本项目中采用的是weixin-java-mp
工具进行开发,下面对其进行配置。
二、配置weixin-java-mp
2.1 在application-dev.yml中添加配置
# 注意不是在spring下!!!wechat:mpAppId: # 上面提到的两个属性复制粘贴进去mpAppSecret:# 注意不是在spring下!!! wechat: mpAppId: # 上面提到的两个属性复制粘贴进去 mpAppSecret:# 注意不是在spring下!!! wechat: mpAppId: # 上面提到的两个属性复制粘贴进去 mpAppSecret:
2.2 引入依赖
在项目pom文件中引入依赖,记得更新重启一下项目?
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-mp</artifactId><version>4.1.0</version></dependency><dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.1.0</version> </dependency><dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>4.1.0</version> </dependency>
2.3 添加工具类和配置类
工具类
package com.oa.wechat.config;/*** @author Charles* @create 2023-05-15-13:05*/import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;@Data@Component@ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值public class WechatAccountConfig {private String mpAppId;private String mpAppSecret;}package com.oa.wechat.config; /** * @author Charles * @create 2023-05-15-13:05 */ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值 public class WechatAccountConfig { private String mpAppId; private String mpAppSecret; }package com.oa.wechat.config; /** * @author Charles * @create 2023-05-15-13:05 */ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Data @Component @ConfigurationProperties(prefix = "wechat") //读取配置配置中写的值 public class WechatAccountConfig { private String mpAppId; private String mpAppSecret; }
配置类
package com.oa.wechat.config;import me.chanjar.weixin.mp.api.WxMpService;import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;import me.chanjar.weixin.mp.config.WxMpConfigStorage;import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.stereotype.Component;@Componentpublic class WeChatMpConfig {@Autowiredprivate WechatAccountConfig wechatAccountConfig;@Beanpublic WxMpService wxMpService(){WxMpService wxMpService = new WxMpServiceImpl();wxMpService.setWxMpConfigStorage(wxMpConfigStorage());return wxMpService;}@Beanpublic WxMpConfigStorage wxMpConfigStorage(){WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl();wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId());wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret());return wxMpConfigStorage;}}package com.oa.wechat.config; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class WeChatMpConfig { @Autowired private WechatAccountConfig wechatAccountConfig; @Bean public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage(){ WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl(); wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId()); wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret()); return wxMpConfigStorage; } }package com.oa.wechat.config; import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.WxMpConfigStorage; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @Component public class WeChatMpConfig { @Autowired private WechatAccountConfig wechatAccountConfig; @Bean public WxMpService wxMpService(){ WxMpService wxMpService = new WxMpServiceImpl(); wxMpService.setWxMpConfigStorage(wxMpConfigStorage()); return wxMpService; } @Bean public WxMpConfigStorage wxMpConfigStorage(){ WxMpDefaultConfigImpl wxMpConfigStorage = new WxMpDefaultConfigImpl(); wxMpConfigStorage.setAppId(wechatAccountConfig.getMpAppId()); wxMpConfigStorage.setSecret(wechatAccountConfig.getMpAppSecret()); return wxMpConfigStorage; } }
后续通过这里配置的WxMpService
来操作微信公众号
三、添加自定义菜单
一个公众号肯定少不了相关的菜单栏,这里步里会讲述如何添加自定义菜单
下图是微信官方指定的菜单格式,由于官方只认这一个格式,所以我们需要将从数据库获取到的数据转换成该格式
3.1 menuController中定义方法
@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制@ApiOperation(value = "同步菜单")@GetMapping("syncMenu")public Result syncMenu() {menuService.syncMenu();return Result.success();}@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制 @ApiOperation(value = "同步菜单") @GetMapping("syncMenu") public Result syncMenu() { menuService.syncMenu(); return Result.success(); }@PreAuthorize("hasAuthority('bnt.menu.syncMenu')") //权限控制 @ApiOperation(value = "同步菜单") @GetMapping("syncMenu") public Result syncMenu() { menuService.syncMenu(); return Result.success(); }
3.2 menuService中重写该方法
@Servicepublic void menuServiceImpl(){@Autowiredprivate WxMpService wxMpService;//获取菜单树形结构@Overridepublic List<MenuVo> findMenuInfo() {List<Menu> menus = weChatMenuMapper.selectList(null);//获取到所有的父级菜单List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0).collect(Collectors.toList());List<MenuVo> menuVoList = new ArrayList<>();for(Menu menu : menusParent){MenuVo menuVo = new MenuVo();BeanUtils.copyProperties(menu,menuVo);//获取到所有的二级目录List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId()).sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());//遍历转换List<MenuVo> children = new ArrayList<>();for(Menu menu1 : subMenuList){MenuVo myMenuVo = new MenuVo();BeanUtils.copyProperties(menu1,myMenuVo);children.add(myMenuVo);}menuVo.setChildren(children);menuVoList.add(menuVo);}return menuVoList;}@Overridepublic void syncMenu() {//将数据库中存储的数据封装为层级关系List<MenuVo> menuVoList = this.findMenuInfo();//菜单JSONArray buttonList = new JSONArray();for(MenuVo oneMenuVo : menuVoList) {JSONObject one = new JSONObject();one.put("name", oneMenuVo.getName());if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) {one.put("type", oneMenuVo.getType());one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl());} else {JSONArray subButton = new JSONArray();for(MenuVo twoMenuVo : oneMenuVo.getChildren()) {JSONObject view = new JSONObject();view.put("type", twoMenuVo.getType());if(twoMenuVo.getType().equals("view")) {view.put("name", twoMenuVo.getName());//H5页面地址 这里队应的是内网穿透的地址 后面会讲view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl())} else {view.put("name", twoMenuVo.getName());view.put("key", twoMenuVo.getMeunKey());}subButton.add(view);}one.put("sub_button", subButton);}buttonList.add(one);}//菜单JSONObject button = new JSONObject();button.put("button", buttonList);try {//固定格式 调用方法来生成菜单wxMpService.getMenuService().menuCreate(button.toJSONString());} catch (WxErrorException e) {throw new RuntimeException(e);}}}@Service public void menuServiceImpl(){ @Autowired private WxMpService wxMpService; //获取菜单树形结构 @Override public List<MenuVo> findMenuInfo() { List<Menu> menus = weChatMenuMapper.selectList(null); //获取到所有的父级菜单 List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0) .collect(Collectors.toList()); List<MenuVo> menuVoList = new ArrayList<>(); for(Menu menu : menusParent){ MenuVo menuVo = new MenuVo(); BeanUtils.copyProperties(menu,menuVo); //获取到所有的二级目录 List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId()) .sorted(Comparator.comparing(Menu::getSort)) .collect(Collectors.toList()); //遍历转换 List<MenuVo> children = new ArrayList<>(); for(Menu menu1 : subMenuList){ MenuVo myMenuVo = new MenuVo(); BeanUtils.copyProperties(menu1,myMenuVo); children.add(myMenuVo); } menuVo.setChildren(children); menuVoList.add(menuVo); } return menuVoList; } @Override public void syncMenu() { //将数据库中存储的数据封装为层级关系 List<MenuVo> menuVoList = this.findMenuInfo(); //菜单 JSONArray buttonList = new JSONArray(); for(MenuVo oneMenuVo : menuVoList) { JSONObject one = new JSONObject(); one.put("name", oneMenuVo.getName()); if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) { one.put("type", oneMenuVo.getType()); one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl()); } else { JSONArray subButton = new JSONArray(); for(MenuVo twoMenuVo : oneMenuVo.getChildren()) { JSONObject view = new JSONObject(); view.put("type", twoMenuVo.getType()); if(twoMenuVo.getType().equals("view")) { view.put("name", twoMenuVo.getName()); //H5页面地址 这里队应的是内网穿透的地址 后面会讲 view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl()) } else { view.put("name", twoMenuVo.getName()); view.put("key", twoMenuVo.getMeunKey()); } subButton.add(view); } one.put("sub_button", subButton); } buttonList.add(one); } //菜单 JSONObject button = new JSONObject(); button.put("button", buttonList); try { //固定格式 调用方法来生成菜单 wxMpService.getMenuService().menuCreate(button.toJSONString()); } catch (WxErrorException e) { throw new RuntimeException(e); } } }@Service public void menuServiceImpl(){ @Autowired private WxMpService wxMpService; //获取菜单树形结构 @Override public List<MenuVo> findMenuInfo() { List<Menu> menus = weChatMenuMapper.selectList(null); //获取到所有的父级菜单 List<Menu> menusParent = menus.stream().filter(menu -> menu.getParentId().longValue() == 0) .collect(Collectors.toList()); List<MenuVo> menuVoList = new ArrayList<>(); for(Menu menu : menusParent){ MenuVo menuVo = new MenuVo(); BeanUtils.copyProperties(menu,menuVo); //获取到所有的二级目录 List<Menu> subMenuList = menus.stream().filter(myMenu -> myMenu.getParentId().longValue() == menu.getId()) .sorted(Comparator.comparing(Menu::getSort)) .collect(Collectors.toList()); //遍历转换 List<MenuVo> children = new ArrayList<>(); for(Menu menu1 : subMenuList){ MenuVo myMenuVo = new MenuVo(); BeanUtils.copyProperties(menu1,myMenuVo); children.add(myMenuVo); } menuVo.setChildren(children); menuVoList.add(menuVo); } return menuVoList; } @Override public void syncMenu() { //将数据库中存储的数据封装为层级关系 List<MenuVo> menuVoList = this.findMenuInfo(); //菜单 JSONArray buttonList = new JSONArray(); for(MenuVo oneMenuVo : menuVoList) { JSONObject one = new JSONObject(); one.put("name", oneMenuVo.getName()); if(CollectionUtils.isEmpty(oneMenuVo.getChildren())) { one.put("type", oneMenuVo.getType()); one.put("url", "http://oa.charles.vip.tunnel/#"+oneMenuVo.getUrl()); } else { JSONArray subButton = new JSONArray(); for(MenuVo twoMenuVo : oneMenuVo.getChildren()) { JSONObject view = new JSONObject(); view.put("type", twoMenuVo.getType()); if(twoMenuVo.getType().equals("view")) { view.put("name", twoMenuVo.getName()); //H5页面地址 这里队应的是内网穿透的地址 后面会讲 view.put("url", "http://oa.charles.vip.tunnel/#"+twoMenuVo.getUrl()) } else { view.put("name", twoMenuVo.getName()); view.put("key", twoMenuVo.getMeunKey()); } subButton.add(view); } one.put("sub_button", subButton); } buttonList.add(one); } //菜单 JSONObject button = new JSONObject(); button.put("button", buttonList); try { //固定格式 调用方法来生成菜单 wxMpService.getMenuService().menuCreate(button.toJSONString()); } catch (WxErrorException e) { throw new RuntimeException(e); } } }
最终效果如下图所示
四、 配置内网穿透
前面我们完成配置公众号的菜单,当点击菜单的时候会跳转到我们本地的页面中,但是由于是内网,外网无法直接访问。这时候可以采用内网穿透技术或者ip来实现功能,当然注册域名更好? 这里以ngrok为例子进行内网穿透 ngrok.cc
4.1 开通隧道(需实名认证)
配置好本地端口,由于我们有两个端口所以需要两条隧道(前端页面,后端接口)
开通成功后会看到隧道id,通过下载客户端然后启动当前隧道就ok了
4.2 配置“授权回调页面域名”
在网页服务-网页账号-修改 处修改,配置的地址为后端接口的地址,比如我后端接口8080对应地址oa.charles.vip.tunnel.com (即复制该地址上去 注意无需前缀http://)
4.3 配置授权回调获取用户信息接口地址
wechat:# 跟着前面配置过的myAppId等# 授权回调获取用户信息接口地址userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfowechat: # 跟着前面配置过的myAppId等 # 授权回调获取用户信息接口地址 userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfowechat: # 跟着前面配置过的myAppId等 # 授权回调获取用户信息接口地址 userInfoUrl: http://oa.charles.vip.tunnel.com/admin/wechat/userInfo
五、编写后端接口
在controller中我们需要定义三个方法,分别是userInfo
— 获取用户信息 OpenId为微信的唯一标识
@GetMapping("/userInfo")public String userInfo(@RequestParam("code") String code,@RequestParam("state") String returnUrl) throws Exception {System.out.println("code " + code);System.out.println("returnUrl " + returnUrl);//获取认证tokenWxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code);//从token中获取openIdString openId = accessToken.getOpenId();//查找user表中是否存在该openIdLambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>();lambdaQueryWrapper.eq(SysUser::getOpenId,openId);SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper);//最终前端都是通过token来判断String token = "";//null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定if(null != sysUser) {token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername());System.out.println("token " + token);}if(returnUrl.indexOf("?") == -1) {return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId;} else {return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId;}}@GetMapping("/userInfo") public String userInfo(@RequestParam("code") String code, @RequestParam("state") String returnUrl) throws Exception { System.out.println("code " + code); System.out.println("returnUrl " + returnUrl); //获取认证token WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); //从token中获取openId String openId = accessToken.getOpenId(); //查找user表中是否存在该openId LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>(); lambdaQueryWrapper.eq(SysUser::getOpenId,openId); SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper); //最终前端都是通过token来判断 String token = ""; //null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定 if(null != sysUser) { token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername()); System.out.println("token " + token); } if(returnUrl.indexOf("?") == -1) { return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId; } else { return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId; } }@GetMapping("/userInfo") public String userInfo(@RequestParam("code") String code, @RequestParam("state") String returnUrl) throws Exception { System.out.println("code " + code); System.out.println("returnUrl " + returnUrl); //获取认证token WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service().getAccessToken(code); //从token中获取openId String openId = accessToken.getOpenId(); //查找user表中是否存在该openId LambdaQueryWrapper<SysUser> lambdaQueryWrapper = new LambdaQueryWrapper<SysUser>(); lambdaQueryWrapper.eq(SysUser::getOpenId,openId); SysUser sysUser = sysUserService.getOne(lambdaQueryWrapper); //最终前端都是通过token来判断 String token = ""; //null != sysUser 说明已经绑定,反之为建立账号绑定,去页面建立账号绑定 if(null != sysUser) { token = JWTHelper.createToken(sysUser.getId(), sysUser.getUsername()); System.out.println("token " + token); } if(returnUrl.indexOf("?") == -1) { return "redirect:" + returnUrl + "?token=" + token + "&openId=" + openId; } else { return "redirect:" + returnUrl + "&token=" + token + "&openId=" + openId; } }
authorize
— 授权登录
@GetMapping("/authorize")public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) {//设置回调URL/* 1. 获取info信息地址* 2. 定义URL类型,这里是UserInfo* 3. 回调地址**/System.out.println("InfoUrl " + InfoUrl);System.out.println("returnUrl " + returnUrl);String redirectUrl = "";try {redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl,WxConsts.OAuth2Scope.SNSAPI_USERINFO,URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8"));System.out.println("微信网页端回调地址 "+redirectUrl);} catch (UnsupportedEncodingException e) {e.printStackTrace();}//跳转return "redirect:" + redirectUrl;}@GetMapping("/authorize") public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) { //设置回调URL /* 1. 获取info信息地址 * 2. 定义URL类型,这里是UserInfo * 3. 回调地址 **/ System.out.println("InfoUrl " + InfoUrl); System.out.println("returnUrl " + returnUrl); String redirectUrl = ""; try { redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8")); System.out.println("微信网页端回调地址 "+redirectUrl); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //跳转 return "redirect:" + redirectUrl; }@GetMapping("/authorize") public String authorize(@RequestParam("returnUrl") String returnUrl, HttpServletRequest request) { //设置回调URL /* 1. 获取info信息地址 * 2. 定义URL类型,这里是UserInfo * 3. 回调地址 **/ System.out.println("InfoUrl " + InfoUrl); System.out.println("returnUrl " + returnUrl); String redirectUrl = ""; try { redirectUrl = wxMpService.getOAuth2Service().buildAuthorizationUrl(InfoUrl, WxConsts.OAuth2Scope.SNSAPI_USERINFO, URLEncoder.encode(returnUrl.replace("guiguoa", "#"), "utf-8")); System.out.println("微信网页端回调地址 "+redirectUrl); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //跳转 return "redirect:" + redirectUrl; }
bindPhone
— 当成功授权后,如果微信号是第一次登录,需要提供窗口给用户用户手机号绑定
@ApiOperation(value = "微信账号绑定手机")@PostMapping("/bindPhone")@ResponseBodypublic Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) {String phone = bindPhoneVo.getPhone();//查找user表中是否有当前记录SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone));if(null != sysUser){//如果用户存在则绑定当前手机号sysUser.setOpenId(bindPhoneVo.getOpenId());sysUserService.updateById(sysUser);}elsereturn Result.fail("找不到当前号码,请联系管理员");return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername()));}@ApiOperation(value = "微信账号绑定手机") @PostMapping("/bindPhone") @ResponseBody public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) { String phone = bindPhoneVo.getPhone(); //查找user表中是否有当前记录 SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone)); if(null != sysUser){ //如果用户存在则绑定当前手机号 sysUser.setOpenId(bindPhoneVo.getOpenId()); sysUserService.updateById(sysUser); }else return Result.fail("找不到当前号码,请联系管理员"); return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername())); }@ApiOperation(value = "微信账号绑定手机") @PostMapping("/bindPhone") @ResponseBody public Result bindPhone(@RequestBody BindPhoneVo bindPhoneVo) { String phone = bindPhoneVo.getPhone(); //查找user表中是否有当前记录 SysUser sysUser = sysUserService.getOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhone, phone)); if(null != sysUser){ //如果用户存在则绑定当前手机号 sysUser.setOpenId(bindPhoneVo.getOpenId()); sysUserService.updateById(sysUser); }else return Result.fail("找不到当前号码,请联系管理员"); return Result.success(JWTHelper.createToken(sysUser.getId(),sysUser.getUsername())); }
当然如果项目中配置了SpringSecurity 还需要在config中排除上述三个方法的路径
六、前端修改(基于vue)
前端的项目中主要修改两个地方,一个是src/App.vue
可以直接copy一下
<template><div id="app"><router-view /><el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" ><el-form ref="dataForm" :model="bindPhoneVo" size="small"><h4>绑定你的手机号</h4><el-form-item label="手机号码"><el-input v-model="bindPhoneVo.phone"/></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button></span></el-dialog></div></template><script>import userInfoApi from '@/api/userInfo'export default {data() {return {show: true,dialogVisible: false,bindPhoneVo: {openId: '',phone: ''}};},created() {// 处理微信授权登录this.wechatLogin();},methods: {wechatLogin() {// 处理微信授权登录let token = this.getQueryString('token') || '';let openId = this.getQueryString('openId') || '';// token === '' && openId != '' 只要这种情况,未绑定账号if(token === '' && openId != '') {// 绑定账号this.bindPhoneVo.openId = openIdthis.dialogVisible = true} else {// 如果绑定了,授权登录直接返回tokenif(token !== '') {window.localStorage.setItem('token', token);}token = window.localStorage.getItem('token') || '';if (token == '') {window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url}}},saveBind() {if(this.bindPhoneVo.phone.length != 11) {alert('手机号码格式不正确')return}userInfoApi.bindPhone(this.bindPhoneVo).then(response => {window.localStorage.setItem('token', response.data);this.dialogVisible = falsewindow.location = 'http://oa.charles.free.idcfengye.com'})},getQueryString (paramName) {if(window.location.href.indexOf('?') == -1) return '';let searchString = window.location.href.split('?')[1];let i, val, params = searchString.split("&");for (i=0;i<params.length;i++) {val = params[i].split("=");if (val[0] == paramName) {return val[1];}}return '';}}};</script><style lang="scss">#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;color: #2c3e50;}</style><template> <div id="app"> <router-view /> <el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" > <el-form ref="dataForm" :model="bindPhoneVo" size="small"> <h4>绑定你的手机号</h4> <el-form-item label="手机号码"> <el-input v-model="bindPhoneVo.phone"/> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button> </span> </el-dialog> </div> </template> <script> import userInfoApi from '@/api/userInfo' export default { data() { return { show: true, dialogVisible: false, bindPhoneVo: { openId: '', phone: '' } }; }, created() { // 处理微信授权登录 this.wechatLogin(); }, methods: { wechatLogin() { // 处理微信授权登录 let token = this.getQueryString('token') || ''; let openId = this.getQueryString('openId') || ''; // token === '' && openId != '' 只要这种情况,未绑定账号 if(token === '' && openId != '') { // 绑定账号 this.bindPhoneVo.openId = openId this.dialogVisible = true } else { // 如果绑定了,授权登录直接返回token if(token !== '') { window.localStorage.setItem('token', token); } token = window.localStorage.getItem('token') || ''; if (token == '') { window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url } } }, saveBind() { if(this.bindPhoneVo.phone.length != 11) { alert('手机号码格式不正确') return } userInfoApi.bindPhone(this.bindPhoneVo).then(response => { window.localStorage.setItem('token', response.data); this.dialogVisible = false window.location = 'http://oa.charles.free.idcfengye.com' }) }, getQueryString (paramName) { if(window.location.href.indexOf('?') == -1) return ''; let searchString = window.location.href.split('?')[1]; let i, val, params = searchString.split("&"); for (i=0;i<params.length;i++) { val = params[i].split("="); if (val[0] == paramName) { return val[1]; } } return ''; } } }; </script> <style lang="scss"> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; } </style><template> <div id="app"> <router-view /> <el-dialog title="绑定手机" :visible.sync="dialogVisible" width="80%" > <el-form ref="dataForm" :model="bindPhoneVo" size="small"> <h4>绑定你的手机号</h4> <el-form-item label="手机号码"> <el-input v-model="bindPhoneVo.phone"/> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button type="primary" icon="el-icon-check" @click="saveBind()" size="small">确 定</el-button> </span> </el-dialog> </div> </template> <script> import userInfoApi from '@/api/userInfo' export default { data() { return { show: true, dialogVisible: false, bindPhoneVo: { openId: '', phone: '' } }; }, created() { // 处理微信授权登录 this.wechatLogin(); }, methods: { wechatLogin() { // 处理微信授权登录 let token = this.getQueryString('token') || ''; let openId = this.getQueryString('openId') || ''; // token === '' && openId != '' 只要这种情况,未绑定账号 if(token === '' && openId != '') { // 绑定账号 this.bindPhoneVo.openId = openId this.dialogVisible = true } else { // 如果绑定了,授权登录直接返回token if(token !== '') { window.localStorage.setItem('token', token); } token = window.localStorage.getItem('token') || ''; if (token == '') { window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url } } }, saveBind() { if(this.bindPhoneVo.phone.length != 11) { alert('手机号码格式不正确') return } userInfoApi.bindPhone(this.bindPhoneVo).then(response => { window.localStorage.setItem('token', response.data); this.dialogVisible = false window.location = 'http://oa.charles.free.idcfengye.com' }) }, getQueryString (paramName) { if(window.location.href.indexOf('?') == -1) return ''; let searchString = window.location.href.split('?')[1]; let i, val, params = searchString.split("&"); for (i=0;i<params.length;i++) { val = params[i].split("="); if (val[0] == paramName) { return val[1]; } } return ''; } } }; </script> <style lang="scss"> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; } </style>
另一个是src/utils/request.js
// 创建axios实例const service = axios.create({baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_urltimeout: 30000 // 请求超时时间});// http response 拦截器service.interceptors.response.use(response => {if (response.data.code == 208) { //登录失败window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url} else {if (response.data.code == 200) {return response.data;} else {// 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示if (response.data.code != 209) {alert(response.data.message || "error");}return Promise.reject(response);}}},error => {return Promise.reject(error.response); // 返回接口返回的错误信息});// 创建axios实例 const service = axios.create({ baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_url timeout: 30000 // 请求超时时间 }); // http response 拦截器 service.interceptors.response.use(response => { if (response.data.code == 208) { //登录失败 window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url } else { if (response.data.code == 200) { return response.data; } else { // 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示 if (response.data.code != 209) { alert(response.data.message || "error"); } return Promise.reject(response); } } }, error => { return Promise.reject(error.response); // 返回接口返回的错误信息 });// 创建axios实例 const service = axios.create({ baseURL: "http://oa.charles.vip.tunnel.com", // api 的 base_url timeout: 30000 // 请求超时时间 }); // http response 拦截器 service.interceptors.response.use(response => { if (response.data.code == 208) { //登录失败 window.location = 'http://oa.charles.vip.tunnel.com/admin/wechat/authorize?returnUrl=' + url } else { if (response.data.code == 200) { return response.data; } else { // 209没有权限 系统会自动跳转授权登录的,已在App.vue处理过,不需要提示 if (response.data.code != 209) { alert(response.data.message || "error"); } return Promise.reject(response); } } }, error => { return Promise.reject(error.response); // 返回接口返回的错误信息 });
由于篇幅过长,我将会在下一篇中讲述如何进行公众号消息推送,感谢各位看到这里?