mirror of
https://github.com/kerwincui/FastBee.git
synced 2025-10-17 21:50:45 +08:00
[功能]:1、跳转url 放到配置文件里面 2、优化登录管理界面 3、添加qq登录解绑和重新绑定功能 4、个人中心增加 qq绑定信息 展示view
This commit is contained in:
@@ -1,15 +1,5 @@
|
|||||||
package com.ruoyi.web.controller.system;
|
package com.ruoyi.web.controller.system;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PutMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
import com.ruoyi.common.annotation.Log;
|
import com.ruoyi.common.annotation.Log;
|
||||||
import com.ruoyi.common.config.RuoYiConfig;
|
import com.ruoyi.common.config.RuoYiConfig;
|
||||||
import com.ruoyi.common.constant.UserConstants;
|
import com.ruoyi.common.constant.UserConstants;
|
||||||
@@ -22,34 +12,42 @@ import com.ruoyi.common.utils.SecurityUtils;
|
|||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.common.utils.file.FileUploadUtils;
|
import com.ruoyi.common.utils.file.FileUploadUtils;
|
||||||
import com.ruoyi.framework.web.service.TokenService;
|
import com.ruoyi.framework.web.service.TokenService;
|
||||||
|
import com.ruoyi.iot.service.IUserSocialProfileService;
|
||||||
import com.ruoyi.system.service.ISysUserService;
|
import com.ruoyi.system.service.ISysUserService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 个人信息 业务处理
|
* 个人信息 业务处理
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/system/user/profile")
|
@RequestMapping("/system/user/profile")
|
||||||
public class SysProfileController extends BaseController
|
public class SysProfileController extends BaseController {
|
||||||
{
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISysUserService userService;
|
private ISysUserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TokenService tokenService;
|
private TokenService tokenService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserSocialProfileService iUserSocialProfileService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 个人信息
|
* 个人信息
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public AjaxResult profile()
|
public AjaxResult profile() {
|
||||||
{
|
|
||||||
LoginUser loginUser = getLoginUser();
|
LoginUser loginUser = getLoginUser();
|
||||||
SysUser user = loginUser.getUser();
|
SysUser user = loginUser.getUser();
|
||||||
AjaxResult ajax = AjaxResult.success(user);
|
AjaxResult ajax = AjaxResult.success(user);
|
||||||
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
|
ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
|
||||||
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
|
ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
|
||||||
|
ajax.put("socialGroup", iUserSocialProfileService.selectUserSocialProfile(loginUser.getUserId()));
|
||||||
return ajax;
|
return ajax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,69 @@
|
|||||||
|
package com.ruoyi.iot.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.iot.service.IUserSocialProfileService;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方登录平台控制Controller
|
||||||
|
*
|
||||||
|
* @author json
|
||||||
|
* @date 2022-04-24
|
||||||
|
*/
|
||||||
|
@Api(tags = "用户社交账户api")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/iot/social/")
|
||||||
|
public class UserSocialController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserSocialProfileService iUserSocialProfileService;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定
|
||||||
|
*
|
||||||
|
* @param bindId 绑定id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/bindId/{bindId}")
|
||||||
|
@ApiOperation("绑定api")
|
||||||
|
@ApiImplicitParam(name = "bindId", value = "绑定bindId", required = true, dataType = "String", paramType = "path", dataTypeClass = String.class)
|
||||||
|
public AjaxResult bindUser(@PathVariable String bindId) {
|
||||||
|
return iUserSocialProfileService.bindUser(bindId, getUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定
|
||||||
|
*
|
||||||
|
* @param platform 绑定类型
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/bind/{platform}")
|
||||||
|
@ApiOperation("绑定跳转api")
|
||||||
|
@ApiImplicitParam(name = "platform", value = "绑定platform", required = true, dataType = "String", paramType = "path", dataTypeClass = String.class)
|
||||||
|
public AjaxResult bind(@PathVariable String platform) {
|
||||||
|
return iUserSocialProfileService.bindSocialAccount(platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解绑
|
||||||
|
*
|
||||||
|
* @param socialUserId 用户社交平台Id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping("/unbind/{socialUserId}")
|
||||||
|
@ApiOperation("解绑api")
|
||||||
|
@ApiImplicitParam(name = "socialUserId", value = "绑定socialId", required = true, dataType = "Long", paramType = "path", dataTypeClass = Long.class)
|
||||||
|
public AjaxResult unbind(@PathVariable Long socialUserId) {
|
||||||
|
return iUserSocialProfileService.unbindSocialAccount(socialUserId, getUserId());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,103 @@
|
|||||||
|
package com.ruoyi.iot.domain;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
|
||||||
|
public class UserSocialProfile {
|
||||||
|
/**
|
||||||
|
* 第三方系统用户表主键
|
||||||
|
*/
|
||||||
|
private Long socialUserId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第三方用户来源
|
||||||
|
*/
|
||||||
|
@Excel(name = "第三方用户来源")
|
||||||
|
private String source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
@Excel(name = "用户名")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
@Excel(name = "用户昵称")
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户头像
|
||||||
|
*/
|
||||||
|
@Excel(name = "用户头像")
|
||||||
|
private String avatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定状态(0:未绑定,1:绑定)
|
||||||
|
*/
|
||||||
|
@Excel(name = "绑定状态(0:未绑定,1:绑定)")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
public Long getSocialUserId() {
|
||||||
|
return socialUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSocialUserId(Long socialUserId) {
|
||||||
|
this.socialUserId = socialUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSource(String source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickname(String nickname) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatar() {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatar(String avatar) {
|
||||||
|
this.avatar = avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
|
||||||
|
.append("socialUserId", getSocialUserId())
|
||||||
|
.append("source", getSource())
|
||||||
|
.append("status", getStatus())
|
||||||
|
.append("username", getUsername())
|
||||||
|
.append("nickname", getNickname())
|
||||||
|
.append("avatar", getAvatar())
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
package com.ruoyi.iot.service;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.iot.domain.UserSocialProfile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IUserSocialProfileService {
|
||||||
|
/**
|
||||||
|
* 找到用户绑定的社交账户
|
||||||
|
*
|
||||||
|
* @param sysUserId 用户主键
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<UserSocialProfile> selectUserSocialProfile(Long sysUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定用户
|
||||||
|
*
|
||||||
|
* @param bindId 绑定的Id
|
||||||
|
* @param sysUserId 用户主键
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AjaxResult bindUser(String bindId, Long sysUserId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击绑定跳转api
|
||||||
|
*
|
||||||
|
* @param platform 平台
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AjaxResult bindSocialAccount(String platform);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除绑定
|
||||||
|
*
|
||||||
|
* @param socialUserId 要解除的社交账户主键
|
||||||
|
* @param sysUserId 用户主键
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AjaxResult unbindSocialAccount(Long socialUserId, Long sysUserId);
|
||||||
|
}
|
@@ -0,0 +1,104 @@
|
|||||||
|
package com.ruoyi.iot.service.impl;
|
||||||
|
|
||||||
|
import com.ruoyi.common.constant.HttpStatus;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.redis.RedisCache;
|
||||||
|
import com.ruoyi.common.enums.SocialPlatformType;
|
||||||
|
import com.ruoyi.iot.domain.SocialUser;
|
||||||
|
import com.ruoyi.iot.domain.UserSocialProfile;
|
||||||
|
import com.ruoyi.iot.model.login.BindIdValue;
|
||||||
|
import com.ruoyi.iot.service.ISocialPlatformService;
|
||||||
|
import com.ruoyi.iot.service.ISocialUserService;
|
||||||
|
import com.ruoyi.iot.service.IUserSocialProfileService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.ruoyi.iot.service.impl.SocialLoginServiceImpl.BIND_REDIS_KEY;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserSocialProfileServiceImpl implements IUserSocialProfileService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
@Autowired
|
||||||
|
private ISocialUserService iSocialUserService;
|
||||||
|
@Autowired
|
||||||
|
private ISocialPlatformService iSocialPlatformService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserSocialProfile> selectUserSocialProfile(Long sysUserId) {
|
||||||
|
SocialUser selectSocialUser = new SocialUser();
|
||||||
|
selectSocialUser.setSysUserId(sysUserId);
|
||||||
|
List<SocialUser> socialUserList = iSocialUserService.selectSocialUserList(selectSocialUser);
|
||||||
|
List<UserSocialProfile> userSocialProfileList = new ArrayList<>();
|
||||||
|
for (SocialUser socialUser : socialUserList) {
|
||||||
|
//如果是删除的标记
|
||||||
|
if (socialUser.getDelFlag().equals("1")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UserSocialProfile userSocialProfile = new UserSocialProfile();
|
||||||
|
userSocialProfile.setSocialUserId(socialUser.getSocialUserId());
|
||||||
|
userSocialProfile.setAvatar(socialUser.getAvatar());
|
||||||
|
userSocialProfile.setSource(socialUser.getSource());
|
||||||
|
userSocialProfile.setUsername(socialUser.getUsername());
|
||||||
|
userSocialProfile.setNickname(socialUser.getNickname());
|
||||||
|
userSocialProfile.setStatus(socialUser.getStatus());
|
||||||
|
userSocialProfileList.add(userSocialProfile);
|
||||||
|
}
|
||||||
|
return userSocialProfileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult bindUser(String bindId, Long sysUserId) {
|
||||||
|
BindIdValue bindValue = redisCache.getCacheObject(BIND_REDIS_KEY + bindId);
|
||||||
|
if (bindValue == null) {
|
||||||
|
//不作提示
|
||||||
|
return AjaxResult.error(HttpStatus.NO_MESSAGE_ALERT, "未知异常");
|
||||||
|
}
|
||||||
|
SocialUser socialUser = findSocialUser(bindValue.getUuid(), bindValue.getSource());
|
||||||
|
SocialUser updateSocialUser = new SocialUser();
|
||||||
|
updateSocialUser.setSocialUserId(socialUser.getSocialUserId());
|
||||||
|
updateSocialUser.setSysUserId(sysUserId);
|
||||||
|
iSocialUserService.updateSocialUser(updateSocialUser);
|
||||||
|
redisCache.deleteObject(BIND_REDIS_KEY + bindId);
|
||||||
|
return AjaxResult.success("绑定成功!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult bindSocialAccount(String platform) {
|
||||||
|
try {
|
||||||
|
SocialPlatformType.valueOf(platform);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return AjaxResult.error("错误平台类型");
|
||||||
|
}
|
||||||
|
return AjaxResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AjaxResult unbindSocialAccount(Long socialUserId, Long sysUserId) {
|
||||||
|
SocialUser socialUser = iSocialUserService.selectSocialUserBySocialUserId(socialUserId);
|
||||||
|
if (socialUser == null) {
|
||||||
|
return AjaxResult.error("绑定账户不存在!");
|
||||||
|
} else if (!socialUser.getSysUserId().equals(socialUserId)) {
|
||||||
|
return AjaxResult.error("用户账户和绑定账户不匹配!");
|
||||||
|
} else {
|
||||||
|
SocialUser updateSocialUser = new SocialUser();
|
||||||
|
updateSocialUser.setSocialUserId(socialUserId);
|
||||||
|
updateSocialUser.setSysUserId(-1L);
|
||||||
|
iSocialUserService.updateSocialUser(updateSocialUser);
|
||||||
|
return AjaxResult.success("解除绑定成功!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SocialUser findSocialUser(String uuid, String source) {
|
||||||
|
SocialUser socialUser = new SocialUser();
|
||||||
|
socialUser.setSource(source);
|
||||||
|
socialUser.setUuid(uuid);
|
||||||
|
List<SocialUser> socialUserList = iSocialUserService.selectSocialUserList(socialUser);
|
||||||
|
return socialUserList == null || socialUserList.isEmpty() ? null : socialUserList.get(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -42,3 +42,27 @@ export function delPlatform(socialPlatformId) {
|
|||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//解除绑定
|
||||||
|
export function unbind(socialUserId){
|
||||||
|
return request({
|
||||||
|
url: '/iot/social/unbind/' + socialUserId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//绑定跳转
|
||||||
|
export function bind(platform){
|
||||||
|
return request({
|
||||||
|
url: '/iot/social/bind/' + platform,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//绑定
|
||||||
|
export function bindUser(bindId){
|
||||||
|
return request({
|
||||||
|
url: '/iot/social/bindId/' + bindId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import { Message } from 'element-ui'
|
import {Message} from 'element-ui'
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { getToken } from '@/utils/auth'
|
import {getToken} from '@/utils/auth'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({showSpinner: false})
|
||||||
|
|
||||||
const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
|
const whiteList = ['/login', '/auth-redirect', '/bind', '/register']
|
||||||
|
|
||||||
@@ -15,7 +15,16 @@ router.beforeEach((to, from, next) => {
|
|||||||
to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
|
to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
|
||||||
/* has token*/
|
/* has token*/
|
||||||
if (to.path === '/login') {
|
if (to.path === '/login') {
|
||||||
next({ path: '/' })
|
if (to.query.bindId) {
|
||||||
|
store.dispatch('BindUser', to.query.bindId).then((res) => {
|
||||||
|
Message.success(res.msg);
|
||||||
|
next({path: '/user/profile'})
|
||||||
|
}).catch(err=>{
|
||||||
|
next({path: '/'})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
next({path: '/'})
|
||||||
|
}
|
||||||
NProgress.done()
|
NProgress.done()
|
||||||
} else {
|
} else {
|
||||||
if (store.getters.roles.length === 0) {
|
if (store.getters.roles.length === 0) {
|
||||||
@@ -24,14 +33,14 @@ router.beforeEach((to, from, next) => {
|
|||||||
store.dispatch('GenerateRoutes').then(accessRoutes => {
|
store.dispatch('GenerateRoutes').then(accessRoutes => {
|
||||||
// 根据roles权限生成可访问的路由表
|
// 根据roles权限生成可访问的路由表
|
||||||
router.addRoutes(accessRoutes) // 动态添加可访问路由表
|
router.addRoutes(accessRoutes) // 动态添加可访问路由表
|
||||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
next({...to, replace: true}) // hack方法 确保addRoutes已完成
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
store.dispatch('LogOut').then(() => {
|
store.dispatch('LogOut').then(() => {
|
||||||
Message.error(err)
|
Message.error(err)
|
||||||
next({ path: '/' })
|
next({path: '/'})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import {bindLogin, getInfo, login, logout, redirectLogin} from '@/api/login'
|
import {login, logout, getInfo, bindLogin, bindRegister, redirectLogin} from '@/api/login'
|
||||||
import {getToken, removeToken, setToken} from '@/utils/auth'
|
import {bindUser} from '@/api/iot/platform'
|
||||||
|
import {getToken, setToken, removeToken} from '@/utils/auth'
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
state: {
|
state: {
|
||||||
@@ -69,6 +70,16 @@ const user = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
BindUser({commit}, bindId) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
bindUser(bindId).then(res => {
|
||||||
|
resolve(res)
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// 获取用户信息
|
// 获取用户信息
|
||||||
GetInfo({commit, state}) {
|
GetInfo({commit, state}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
@@ -36,7 +36,8 @@
|
|||||||
size="mini"
|
size="mini"
|
||||||
@click="handleAdd"
|
@click="handleAdd"
|
||||||
v-hasPermi="['iot:platform:add']"
|
v-hasPermi="['iot:platform:add']"
|
||||||
>新增</el-button>
|
>新增
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -47,7 +48,8 @@
|
|||||||
:disabled="single"
|
:disabled="single"
|
||||||
@click="handleUpdate"
|
@click="handleUpdate"
|
||||||
v-hasPermi="['iot:platform:edit']"
|
v-hasPermi="['iot:platform:edit']"
|
||||||
>修改</el-button>
|
>修改
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -58,7 +60,8 @@
|
|||||||
:disabled="multiple"
|
:disabled="multiple"
|
||||||
@click="handleDelete"
|
@click="handleDelete"
|
||||||
v-hasPermi="['iot:platform:remove']"
|
v-hasPermi="['iot:platform:remove']"
|
||||||
>删除</el-button>
|
>删除
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -68,43 +71,49 @@
|
|||||||
size="mini"
|
size="mini"
|
||||||
@click="handleExport"
|
@click="handleExport"
|
||||||
v-hasPermi="['iot:platform:export']"
|
v-hasPermi="['iot:platform:export']"
|
||||||
>导出</el-button>
|
>导出
|
||||||
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="platformList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="platformList" @selection-change="handleSelectionChange">
|
||||||
<el-table-column type="selection" width="55" align="center"/>
|
<el-table-column type="selection" width="55" align="center"/>
|
||||||
<el-table-column align="center" label="登录平台主键" prop="socialPlatformId"/>
|
<el-table-column align="center" label="平台主键" prop="socialPlatformId" width="75"/>
|
||||||
<el-table-column align="center" label="第三方平台" prop="platform">
|
<el-table-column align="center" label="平台名称" prop="platform" width="95">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.iot_social_platform" :value="scope.row.platform"/>
|
<dict-tag :options="dict.type.iot_social_platform" :value="scope.row.platform"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="状态" prop="status">
|
|
||||||
|
<el-table-column align="center" label="状态" prop="status" width="75">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<dict-tag :options="dict.type.iot_social_platform_status" :value="scope.row.status"/>
|
<dict-tag :options="dict.type.iot_social_platform_status" :value="scope.row.status"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="第三方平台申请Id" align="center" prop="clientId"/>
|
<el-table-column label="平台申请Id" align="center" prop="clientId"/>
|
||||||
<el-table-column label="第三方平台密钥" align="center" prop="secretKey"/>
|
<el-table-column label="平台密钥" align="center" prop="secretKey" :show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="用户认证后跳转地址" align="center" prop="redirectUri"/>
|
<el-table-column label="跳转地址" align="center" prop="redirectUri" width="180" :show-overflow-tooltip="true"/>
|
||||||
|
|
||||||
|
<el-table-column align="center" label="绑定登录uri" prop="bindUri" :show-tooltip-when-overflow="true"
|
||||||
|
:render-header="(h,column)=>renderHeaderMethods(h,column,columnTips.bindId)"/>
|
||||||
|
<el-table-column align="center" label="跳转登录uri" prop="redirectLoginUri" :show-tooltip-when-overflow="true"
|
||||||
|
:render-header="(h,column)=>renderHeaderMethods(h,column,columnTips.redirectLogin)"/>
|
||||||
|
<el-table-column align="center" label="错误提示uri" prop="errorMsgUri" :show-tooltip-when-overflow="true"
|
||||||
|
:render-header="(h,column)=>renderHeaderMethods(h,column,columnTips.errorId)"/>
|
||||||
|
<el-table-column align="center" label="备注" prop="remark" width="75" :show-tooltip-when-overflow="true"/>
|
||||||
<el-table-column align="center" label="创建者" prop="createBy"/>
|
<el-table-column align="center" label="创建者" prop="createBy"/>
|
||||||
<el-table-column align="center" label="创建时间" prop="createTime" width="180">
|
<el-table-column align="center" label="创建时间" prop="createTime" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="更新时间" prop="updateTime" width="180">
|
<el-table-column align="center" label="更新者" prop="updateBy"/>
|
||||||
|
<el-table-column align="center" label="更新时间" prop="updateTime" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.updateTime, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="更新者" prop="updateBy"/>
|
|
||||||
<el-table-column align="center" label="备注" prop="remark"/>
|
|
||||||
<el-table-column align="center" label="绑定注册登录uri,http://localhost/login?bindId=" prop="bindUri"/>
|
|
||||||
<el-table-column align="center" label="跳转登录uri,http://localhost/login?loginId=" prop="redirectLoginUri"/>
|
|
||||||
<el-table-column align="center" label="错误提示uri,http://localhost/login?errorId=" prop="errorMsgUri"/>
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -121,7 +130,8 @@
|
|||||||
icon="el-icon-delete"
|
icon="el-icon-delete"
|
||||||
@click="handleDelete(scope.row)"
|
@click="handleDelete(scope.row)"
|
||||||
v-hasPermi="['iot:platform:remove']"
|
v-hasPermi="['iot:platform:remove']"
|
||||||
>删除</el-button>
|
>删除
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -223,6 +233,11 @@ export default {
|
|||||||
platform: null,
|
platform: null,
|
||||||
status: null,
|
status: null,
|
||||||
},
|
},
|
||||||
|
columnTips: {
|
||||||
|
bindId: "绑定登录uri, http://localhost/login?bindId=,域名换成对应域名即可,本地开发不需要更改",
|
||||||
|
redirectLogin: "跳转登录uri,http://localhost/login?loginId=,域名换成对应域名即可,本地开发不需要更改",
|
||||||
|
errorId: "错误提示获取uri,http://localhost/login?errorId=,域名换成对应域名即可,本地开发不需要更改"
|
||||||
|
},
|
||||||
// 表单参数
|
// 表单参数
|
||||||
form: {},
|
form: {},
|
||||||
// 表单校验
|
// 表单校验
|
||||||
@@ -258,6 +273,25 @@ export default {
|
|||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
renderHeaderMethods(h, {column}, content) {
|
||||||
|
return h(
|
||||||
|
'div', [
|
||||||
|
h('span', column.label),
|
||||||
|
h('el-tooltip', {
|
||||||
|
props: {
|
||||||
|
effect: 'dark',
|
||||||
|
content: content,
|
||||||
|
placement: 'top'
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h('i', {
|
||||||
|
class: 'el-icon-question',
|
||||||
|
style: 'color:#409EFF;margin-left:5px;'
|
||||||
|
})
|
||||||
|
])
|
||||||
|
]
|
||||||
|
);
|
||||||
|
},
|
||||||
/** 查询第三方登录平台控制列表 */
|
/** 查询第三方登录平台控制列表 */
|
||||||
getList() {
|
getList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
@@ -266,12 +300,14 @@ export default {
|
|||||||
this.total = response.total;
|
this.total = response.total;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
,
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
cancel() {
|
cancel() {
|
||||||
this.open = false;
|
this.open = false;
|
||||||
this.reset();
|
this.reset();
|
||||||
},
|
}
|
||||||
|
,
|
||||||
// 表单重置
|
// 表单重置
|
||||||
reset() {
|
reset() {
|
||||||
this.form = {
|
this.form = {
|
||||||
@@ -292,29 +328,34 @@ export default {
|
|||||||
errorMsgUri: null
|
errorMsgUri: null
|
||||||
};
|
};
|
||||||
this.resetForm("form");
|
this.resetForm("form");
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 重置按钮操作 */
|
/** 重置按钮操作 */
|
||||||
resetQuery() {
|
resetQuery() {
|
||||||
this.resetForm("queryForm");
|
this.resetForm("queryForm");
|
||||||
this.handleQuery();
|
this.handleQuery();
|
||||||
},
|
}
|
||||||
|
,
|
||||||
// 多选框选中数据
|
// 多选框选中数据
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.ids = selection.map(item => item.socialPlatformId)
|
this.ids = selection.map(item => item.socialPlatformId)
|
||||||
this.single = selection.length!==1
|
this.single = selection.length !== 1
|
||||||
this.multiple = !selection.length
|
this.multiple = !selection.length
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 新增按钮操作 */
|
/** 新增按钮操作 */
|
||||||
handleAdd() {
|
handleAdd() {
|
||||||
this.reset();
|
this.reset();
|
||||||
this.open = true;
|
this.open = true;
|
||||||
this.title = "添加第三方登录平台控制";
|
this.title = "添加第三方登录平台控制";
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 修改按钮操作 */
|
/** 修改按钮操作 */
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
this.reset();
|
this.reset();
|
||||||
@@ -324,7 +365,8 @@ export default {
|
|||||||
this.open = true;
|
this.open = true;
|
||||||
this.title = "修改第三方登录平台控制";
|
this.title = "修改第三方登录平台控制";
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 提交按钮 */
|
/** 提交按钮 */
|
||||||
submitForm() {
|
submitForm() {
|
||||||
this.$refs["form"].validate(valid => {
|
this.$refs["form"].validate(valid => {
|
||||||
@@ -344,17 +386,20 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
,
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const socialPlatformIds = row.socialPlatformId || this.ids;
|
const socialPlatformIds = row.socialPlatformId || this.ids;
|
||||||
this.$modal.confirm('是否确认删除第三方登录平台控制编号为"' + socialPlatformIds + '"的数据项?').then(function() {
|
this.$modal.confirm('是否确认删除第三方登录平台控制编号为"' + socialPlatformIds + '"的数据项?').then(function () {
|
||||||
return delPlatform(socialPlatformIds);
|
return delPlatform(socialPlatformIds);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList();
|
this.getList();
|
||||||
this.$modal.msgSuccess("删除成功");
|
this.$modal.msgSuccess("删除成功");
|
||||||
}).catch(() => {});
|
}).catch(() => {
|
||||||
},
|
});
|
||||||
|
}
|
||||||
|
,
|
||||||
/** 导出按钮操作 */
|
/** 导出按钮操作 */
|
||||||
handleExport() {
|
handleExport() {
|
||||||
this.download('iot/platform/export', {
|
this.download('iot/platform/export', {
|
||||||
|
@@ -8,33 +8,62 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<userAvatar :user="user" />
|
<userAvatar :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
<ul class="list-group list-group-striped">
|
<ul class="list-group list-group-striped">
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="user" />用户名称
|
<svg-icon icon-class="user"/>
|
||||||
|
用户名称
|
||||||
<div class="pull-right">{{ user.userName }}</div>
|
<div class="pull-right">{{ user.userName }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="phone" />手机号码
|
<svg-icon icon-class="phone"/>
|
||||||
|
手机号码
|
||||||
<div class="pull-right">{{ user.phonenumber }}</div>
|
<div class="pull-right">{{ user.phonenumber }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="email" />用户邮箱
|
<svg-icon icon-class="email"/>
|
||||||
|
用户邮箱
|
||||||
<div class="pull-right">{{ user.email }}</div>
|
<div class="pull-right">{{ user.email }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="tree" />所属部门
|
<svg-icon icon-class="tree"/>
|
||||||
|
所属部门
|
||||||
<div class="pull-right" v-if="user.dept">{{ user.dept.deptName }} / {{ postGroup }}</div>
|
<div class="pull-right" v-if="user.dept">{{ user.dept.deptName }} / {{ postGroup }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="peoples" />所属角色
|
<svg-icon icon-class="peoples"/>
|
||||||
|
所属角色
|
||||||
<div class="pull-right">{{ roleGroup }}</div>
|
<div class="pull-right">{{ roleGroup }}</div>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<svg-icon icon-class="date" />创建日期
|
<svg-icon icon-class="date"/>
|
||||||
|
创建日期
|
||||||
<div class="pull-right">{{ user.createTime }}</div>
|
<div class="pull-right">{{ user.createTime }}</div>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="qq"/>
|
||||||
|
QQ
|
||||||
|
<div class="pull-right" v-if="!this.qqBind">
|
||||||
|
<el-button size="mini" style="margin-top: -7px" @click="bind(socialAccount.QQ.name)">未绑定</el-button>
|
||||||
|
</div>
|
||||||
|
<div style="float:right;display: flex;" v-else>
|
||||||
|
<span>{{ this.socialAccount.QQ.value.nickname }}</span>
|
||||||
|
<el-image style="width: 25px; height: 25px; margin-top: -7px;margin-left: 5px;margin-right: 5px"
|
||||||
|
:src="this.socialAccount.QQ.value.avatar"/>
|
||||||
|
<el-button style="margin-top: -5px" size="mini" @click="unbind(socialAccount.QQ.name)">解除绑定
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<svg-icon icon-class="wechat"/>
|
||||||
|
微信
|
||||||
|
<div class="pull-right">
|
||||||
|
<el-button style="margin-top: -7px" size="mini">未绑定</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -46,10 +75,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<el-tabs v-model="activeTab">
|
<el-tabs v-model="activeTab">
|
||||||
<el-tab-pane label="基本资料" name="userinfo">
|
<el-tab-pane label="基本资料" name="userinfo">
|
||||||
<userInfo :user="user" />
|
<userInfo :user="user"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="修改密码" name="resetPwd">
|
<el-tab-pane label="修改密码" name="resetPwd">
|
||||||
<resetPwd :user="user" />
|
<resetPwd :user="user"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -62,17 +91,35 @@
|
|||||||
import userAvatar from "./userAvatar";
|
import userAvatar from "./userAvatar";
|
||||||
import userInfo from "./userInfo";
|
import userInfo from "./userInfo";
|
||||||
import resetPwd from "./resetPwd";
|
import resetPwd from "./resetPwd";
|
||||||
import { getUserProfile } from "@/api/system/user";
|
import {getUserProfile} from "@/api/system/user";
|
||||||
|
import {unbind, bind} from "@/api/iot/platform";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Profile",
|
name: "Profile",
|
||||||
components: { userAvatar, userInfo, resetPwd },
|
components: {userAvatar, userInfo, resetPwd},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
user: {},
|
user: {},
|
||||||
roleGroup: {},
|
roleGroup: {},
|
||||||
postGroup: {},
|
postGroup: {},
|
||||||
activeTab: "userinfo"
|
activeTab: "userinfo",
|
||||||
|
socialGroup: [],
|
||||||
|
qqBind: false,
|
||||||
|
wechatBind: false,
|
||||||
|
socialAccount: {
|
||||||
|
QQ: {
|
||||||
|
name: "QQ",
|
||||||
|
value: null
|
||||||
|
},
|
||||||
|
Wechat: {
|
||||||
|
name: "Wechat",
|
||||||
|
value: null
|
||||||
|
},
|
||||||
|
Status: {
|
||||||
|
bind: "0",
|
||||||
|
unbind: "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -84,7 +131,53 @@ export default {
|
|||||||
this.user = response.data;
|
this.user = response.data;
|
||||||
this.roleGroup = response.roleGroup;
|
this.roleGroup = response.roleGroup;
|
||||||
this.postGroup = response.postGroup;
|
this.postGroup = response.postGroup;
|
||||||
|
this.socialGroup = response.socialGroup;
|
||||||
|
if (!(this.socialGroup === undefined || this.socialGroup === null)) {
|
||||||
|
this.socialGroup.forEach(social => {
|
||||||
|
if (social.status === this.socialAccount.Status.bind) {
|
||||||
|
switch (social.source) {
|
||||||
|
case this.socialAccount.QQ.name: {
|
||||||
|
this.qqBind = true;
|
||||||
|
this.socialAccount.QQ.value = social;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case this.socialAccount.Wechat.name: {
|
||||||
|
this.wechatBind = true;
|
||||||
|
this.socialAccount.Wechat.value = social;
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
bind(type) {
|
||||||
|
switch (type) {
|
||||||
|
case this.socialAccount.QQ.name: {
|
||||||
|
if (!this.qqBind) {
|
||||||
|
bind(type).then((res) => {
|
||||||
|
window.location.href = "http://localhost:8080/auth/render/qq";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
unbind(type) {
|
||||||
|
switch (type) {
|
||||||
|
case this.socialAccount.QQ.name: {
|
||||||
|
if (this.qqBind) {
|
||||||
|
unbind(this.socialAccount.QQ.value.socialUserId).then((res) => {
|
||||||
|
this.$modal.msgSuccess(res.msg);
|
||||||
|
this.qqBind = false;
|
||||||
|
this.socialAccount.QQ.value = null;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user