mirror of https://github.com/mindoc-org/mindoc.git
commit
bae6826a97
|
@ -224,6 +224,12 @@ dingtalk_app_secret="${MINDOC_DINGTALK_APPSECRET}"
|
||||||
# 钉钉登录默认只读账号
|
# 钉钉登录默认只读账号
|
||||||
dingtalk_tmp_reader="${MINDOC_DINGTALK_READER}"
|
dingtalk_tmp_reader="${MINDOC_DINGTALK_READER}"
|
||||||
|
|
||||||
|
# 钉钉扫码登录Key
|
||||||
|
dingtalk_qr_key="${MINDOC_DINGTALK_QRKEY}"
|
||||||
|
|
||||||
|
# 钉钉扫码登录Secret
|
||||||
|
dingtalk_qr_secret="${MINDOC_DINGTALK_QRSECRET}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,12 @@ func (c *AccountController) referer() string {
|
||||||
func (c *AccountController) Prepare() {
|
func (c *AccountController) Prepare() {
|
||||||
c.BaseController.Prepare()
|
c.BaseController.Prepare()
|
||||||
c.EnableXSRF = beego.AppConfig.DefaultBool("enablexsrf", true)
|
c.EnableXSRF = beego.AppConfig.DefaultBool("enablexsrf", true)
|
||||||
|
|
||||||
c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
|
c.Data["xsrfdata"] = template.HTML(c.XSRFFormHTML())
|
||||||
c.Data["corpID"] = beego.AppConfig.String("dingtalk_corpid")
|
c.Data["corpID"] = beego.AppConfig.String("dingtalk_corpid")
|
||||||
|
c.Data["ENABLE_QR_DINGTALK"] = (beego.AppConfig.String("dingtalk_corpid") != "")
|
||||||
|
c.Data["dingtalk_qr_key"] = beego.AppConfig.String("dingtalk_qr_key")
|
||||||
|
|
||||||
if !c.EnableXSRF {
|
if !c.EnableXSRF {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -165,14 +169,14 @@ func (c *AccountController) DingTalkLogin() {
|
||||||
|
|
||||||
userid, err := dingtalkAgent.GetUserIDByCode(code)
|
userid, err := dingtalkAgent.GetUserIDByCode(code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
beego.Warn("钉钉自动登录失败 ->", err)
|
beego.Warn("获取钉钉用户ID失败 ->", err)
|
||||||
c.JsonResult(500, "自动登录失败", nil)
|
c.JsonResult(500, "自动登录失败", nil)
|
||||||
c.StopRun()
|
c.StopRun()
|
||||||
}
|
}
|
||||||
|
|
||||||
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
|
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
beego.Warn("钉钉自动登录失败 ->", err)
|
beego.Warn("获取钉钉用户信息失败 ->", err)
|
||||||
c.JsonResult(500, "自动登录失败", nil)
|
c.JsonResult(500, "自动登录失败", nil)
|
||||||
c.StopRun()
|
c.StopRun()
|
||||||
}
|
}
|
||||||
|
@ -191,6 +195,79 @@ func (c *AccountController) DingTalkLogin() {
|
||||||
c.JsonResult(0, "ok", username)
|
c.JsonResult(0, "ok", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QR二维码登录
|
||||||
|
func (c *AccountController) QRLogin() {
|
||||||
|
c.Prepare()
|
||||||
|
|
||||||
|
appName := c.Ctx.Input.Param(":app")
|
||||||
|
|
||||||
|
switch appName {
|
||||||
|
// 钉钉扫码登录
|
||||||
|
case "dingtalk":
|
||||||
|
code := c.GetString("code")
|
||||||
|
state := c.GetString("state")
|
||||||
|
if state != "1" || code == "" {
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
appKey := beego.AppConfig.String("dingtalk_qr_key")
|
||||||
|
appSecret := beego.AppConfig.String("dingtalk_qr_secret")
|
||||||
|
|
||||||
|
qrDingtalk := dingtalk.NewDingtalkQRLogin(appSecret, appKey)
|
||||||
|
unionID, err := qrDingtalk.GetUnionIDByCode(code)
|
||||||
|
if err != nil {
|
||||||
|
beego.Warn("获取钉钉临时UnionID失败 ->", err)
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
|
||||||
|
appKey = beego.AppConfig.String("dingtalk_app_key")
|
||||||
|
appSecret = beego.AppConfig.String("dingtalk_app_secret")
|
||||||
|
tmpReader := beego.AppConfig.String("dingtalk_tmp_reader")
|
||||||
|
|
||||||
|
dingtalkAgent := dingtalk.NewDingTalkAgent(appSecret, appKey)
|
||||||
|
err = dingtalkAgent.GetAccesstoken()
|
||||||
|
if err != nil {
|
||||||
|
beego.Warn("获取钉钉临时Token失败 ->", err)
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
|
||||||
|
userid, err := dingtalkAgent.GetUserIDByUnionID(unionID)
|
||||||
|
if err != nil {
|
||||||
|
beego.Warn("获取钉钉用户ID失败 ->", err)
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
|
||||||
|
username, avatar, err := dingtalkAgent.GetUserNameAndAvatarByUserID(userid)
|
||||||
|
if err != nil {
|
||||||
|
beego.Warn("获取钉钉用户信息失败 ->", err)
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
|
||||||
|
member, err := models.NewMember().TmpLogin(tmpReader)
|
||||||
|
if err == nil {
|
||||||
|
member.LastLoginTime = time.Now()
|
||||||
|
_ = member.Update("last_login_time")
|
||||||
|
member.Account = username
|
||||||
|
if avatar != "" {
|
||||||
|
member.Avatar = avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
c.SetMember(*member)
|
||||||
|
c.LoggedIn(false)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
|
||||||
|
default:
|
||||||
|
c.Redirect(conf.URLFor("AccountController.Login"), 302)
|
||||||
|
c.StopRun()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 登录成功后的操作,如重定向到原始请求页面
|
// 登录成功后的操作,如重定向到原始请求页面
|
||||||
func (c *AccountController) LoggedIn(isPost bool) interface{} {
|
func (c *AccountController) LoggedIn(isPost bool) interface{} {
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ func init() {
|
||||||
|
|
||||||
beego.Router("/login", &controllers.AccountController{}, "*:Login")
|
beego.Router("/login", &controllers.AccountController{}, "*:Login")
|
||||||
beego.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
|
beego.Router("/dingtalk_login", &controllers.AccountController{}, "*:DingTalkLogin")
|
||||||
|
beego.Router("/qrlogin/:app", &controllers.AccountController{}, "*:QRLogin")
|
||||||
beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
|
beego.Router("/logout", &controllers.AccountController{}, "*:Logout")
|
||||||
beego.Router("/register", &controllers.AccountController{}, "*:Register")
|
beego.Router("/register", &controllers.AccountController{}, "*:Register")
|
||||||
beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
|
beego.Router("/find_password", &controllers.AccountController{}, "*:FindPassword")
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
!function (window, document) {
|
||||||
|
function d(a) {
|
||||||
|
var e, c = document.createElement("iframe"),
|
||||||
|
d = "https://login.dingtalk.com/login/qrcode.htm?goto=" + a.goto ;
|
||||||
|
d += a.style ? "&style=" + encodeURIComponent(a.style) : "",
|
||||||
|
d += a.href ? "&href=" + a.href : "",
|
||||||
|
c.src = d,
|
||||||
|
c.frameBorder = "0",
|
||||||
|
c.allowTransparency = "true",
|
||||||
|
c.scrolling = "no",
|
||||||
|
c.width = a.width ? a.width + 'px' : "365px",
|
||||||
|
c.height = a.height ? a.height + 'px' : "400px",
|
||||||
|
e = document.getElementById(a.id),
|
||||||
|
e.innerHTML = "",
|
||||||
|
e.appendChild(c)
|
||||||
|
}
|
||||||
|
window.DDLogin = d
|
||||||
|
}(window, document);
|
|
@ -10,6 +10,9 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DingTalkAgent 用于钉钉交互
|
// DingTalkAgent 用于钉钉交互
|
||||||
|
@ -106,6 +109,46 @@ func (d *DingTalkAgent) GetUserNameAndAvatarByUserID(userid string) (string, str
|
||||||
return username, avatar, nil
|
return username, avatar, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserIDByUnionID 根据UnionID获取用户Userid
|
||||||
|
func (d *DingTalkAgent) GetUserIDByUnionID(unionid string) (string, error) {
|
||||||
|
urlEndpoint, err := url.Parse("https://oapi.dingtalk.com/topapi/user/getbyunionid")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
query := url.Values{}
|
||||||
|
query.Set("access_token", d.AccessToken)
|
||||||
|
urlEndpoint.RawQuery = query.Encode()
|
||||||
|
urlPath := urlEndpoint.String()
|
||||||
|
|
||||||
|
resp, err := http.PostForm(urlPath, url.Values{"unionid": {unionid}})
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// 解析钉钉返回数据
|
||||||
|
var rdata map[string]interface{}
|
||||||
|
err = json.Unmarshal(body, &rdata)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
errcode := rdata["errcode"].(float64)
|
||||||
|
if errcode != 0 {
|
||||||
|
return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
|
||||||
|
}
|
||||||
|
|
||||||
|
result := rdata["result"].(map[string]interface{})
|
||||||
|
if result["contact_type"].(float64) != 0 {
|
||||||
|
return "", errors.New("该用户不属于企业内部员工,无法登录。")
|
||||||
|
}
|
||||||
|
userid := result["userid"].(string)
|
||||||
|
return userid, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetAccesstoken 获取钉钉请求Token
|
// GetAccesstoken 获取钉钉请求Token
|
||||||
func (d *DingTalkAgent) GetAccesstoken() (err error) {
|
func (d *DingTalkAgent) GetAccesstoken() (err error) {
|
||||||
|
|
||||||
|
@ -132,16 +175,71 @@ func (d *DingTalkAgent) GetAccesstoken() (err error) {
|
||||||
return errors.New("accesstoken获取错误:" + i["errmsg"].(string))
|
return errors.New("accesstoken获取错误:" + i["errmsg"].(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DingTalkAgent) encodeSHA256(message string) string {
|
// DingtalkQRLogin 用于钉钉扫码登录
|
||||||
|
type DingtalkQRLogin struct {
|
||||||
|
AppSecret string
|
||||||
|
AppKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDingtalkQRLogin 构造钉钉扫码登录实例
|
||||||
|
func NewDingtalkQRLogin(appSecret, appKey string) DingtalkQRLogin {
|
||||||
|
return DingtalkQRLogin{
|
||||||
|
AppSecret: appSecret,
|
||||||
|
AppKey: appKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUnionIDByCode 获取扫码用户UnionID
|
||||||
|
func (d *DingtalkQRLogin) GetUnionIDByCode(code string) (userid string, err error) {
|
||||||
|
var resp *http.Response
|
||||||
|
//服务端通过临时授权码获取授权用户的个人信息
|
||||||
|
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10) // 毫秒时间戳
|
||||||
|
signature := d.encodeSHA256(timestamp) // 加密签名
|
||||||
|
urlPath := fmt.Sprintf(
|
||||||
|
"https://oapi.dingtalk.com/sns/getuserinfo_bycode?accessKey=%s×tamp=%s&signature=%s",
|
||||||
|
d.AppKey, timestamp, signature)
|
||||||
|
|
||||||
|
// 构造请求数据
|
||||||
|
param := struct {
|
||||||
|
Tmp_auth_code string `json:"tmp_auth_code"`
|
||||||
|
}{code}
|
||||||
|
paraByte, _ := json.Marshal(param)
|
||||||
|
paraString := string(paraByte)
|
||||||
|
|
||||||
|
resp, err = http.Post(urlPath, "application/json;charset=UTF-8", strings.NewReader(paraString))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析钉钉返回数据
|
||||||
|
var rdata map[string]interface{}
|
||||||
|
err = json.Unmarshal(body, &rdata)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
errcode := rdata["errcode"].(float64)
|
||||||
|
if errcode != 0 {
|
||||||
|
return "", errors.New(fmt.Sprintf("登录错误: %.0f, %s", errcode, rdata["errmsg"].(string)))
|
||||||
|
}
|
||||||
|
unionid := rdata["user_info"].(map[string]interface{})["unionid"].(string)
|
||||||
|
return unionid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DingtalkQRLogin) encodeSHA256(timestamp string) string {
|
||||||
// 钉钉签名算法实现
|
// 钉钉签名算法实现
|
||||||
h := hmac.New(sha256.New, []byte(d.AppSecret))
|
h := hmac.New(sha256.New, []byte(d.AppSecret))
|
||||||
h.Write([]byte(message))
|
h.Write([]byte(timestamp))
|
||||||
sum := h.Sum(nil) // 二进制流
|
sum := h.Sum(nil) // 二进制流
|
||||||
tmpMsg := base64.StdEncoding.EncodeToString(sum)
|
tmpMsg := base64.StdEncoding.EncodeToString(sum)
|
||||||
|
|
||||||
uv := url.Values{}
|
uv := url.Values{}
|
||||||
uv.Add("0", tmpMsg)
|
uv.Add("0", tmpMsg)
|
||||||
message = uv.Encode()[2:]
|
message := uv.Encode()[2:]
|
||||||
|
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,11 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="正在登录..." autocomplete="off">立即登录</button>
|
<button type="button" id="btn-login" class="btn btn-success" style="width: 100%" data-loading-text="正在登录..." autocomplete="off">立即登录</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{if .ENABLE_QR_DINGTALK}}
|
||||||
|
<div class="form-group">
|
||||||
|
<a id="btn-dingtalk-qr" class="btn btn-default" style="width: 100%" data-loading-text="" autocomplete="off">钉钉扫码登录</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
{{if .ENABLED_REGISTER}}
|
{{if .ENABLED_REGISTER}}
|
||||||
{{if ne .ENABLED_REGISTER "false"}}
|
{{if ne .ENABLED_REGISTER "false"}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -78,6 +83,10 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</form>
|
</form>
|
||||||
|
<div class="form-group dingtalk-container" style="display: none;">
|
||||||
|
<div id="dingtalk-qr-container"></div>
|
||||||
|
<a class="btn btn-default btn-dingtalk" style="width: 100%" data-loading-text="" autocomplete="off">返回账号密码登录</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
@ -87,46 +96,74 @@
|
||||||
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
|
<script src="{{cdnjs "/static/bootstrap/js/bootstrap.min.js"}}" type="text/javascript"></script>
|
||||||
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript"></script>
|
<script src="{{cdnjs "/static/layer/layer.js"}}" type="text/javascript"></script>
|
||||||
<script src="{{cdnjs "/static/js/dingtalk-jsapi.js"}}" type="text/javascript"></script>
|
<script src="{{cdnjs "/static/js/dingtalk-jsapi.js"}}" type="text/javascript"></script>
|
||||||
|
<script src="{{cdnjs "/static/js/dingtalk-ddlogin.js"}}" type="text/javascript"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function () {
|
if (dd.env.platform !== "notInDingTalk"){
|
||||||
if (dd.env.platform !== "notInDingTalk"){
|
dd.ready(function() {
|
||||||
dd.ready(function() {
|
dd.runtime.permission.requestAuthCode({
|
||||||
dd.runtime.permission.requestAuthCode({
|
corpId: {{ .corpID }} , // 企业id
|
||||||
corpId: {{ .corpID }} , // 企业id
|
onSuccess: function (info) {
|
||||||
onSuccess: function (info) {
|
var index = layer.load(1, {
|
||||||
var index = layer.load(1, {
|
shade: [0.1, '#fff'] // 0.1 透明度的白色背景
|
||||||
shade: [0.1, '#fff'] // 0.1 透明度的白色背景
|
})
|
||||||
})
|
|
||||||
|
|
||||||
var formData = $("form").serializeArray()
|
var formData = $("form").serializeArray()
|
||||||
formData.push({"name": "dingtalk_code", "value": info.code})
|
formData.push({"name": "dingtalk_code", "value": info.code})
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "{{urlfor "AccountController.DingTalkLogin"}} ",
|
url: "{{urlfor "AccountController.DingTalkLogin"}} ",
|
||||||
data: formData,
|
data: formData,
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
type: "POST",
|
type: "POST",
|
||||||
complete: function(){
|
complete: function(){
|
||||||
layer.close(index)
|
layer.close(index)
|
||||||
},
|
},
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
if (res.errcode !== 0) {
|
if (res.errcode !== 0) {
|
||||||
layer.msg(res.message)
|
layer.msg(res.message)
|
||||||
} else {
|
} else {
|
||||||
window.location = "{{ urlfor "HomeController.Index" }}"
|
window.location = "{{ urlfor "HomeController.Index" }}"
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (res) {
|
|
||||||
layer.msg("发生异常")
|
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
}
|
error: function (res) {
|
||||||
});
|
layer.msg("发生异常")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
})
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var url = 'https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid={{.dingtalk_qr_key}}&response_type=code&scope=snsapi_login&state=1&redirect_uri={{ urlfor "AccountController.QRLogin" ":app" "dingtalk"}}'
|
||||||
|
var obj = DDLogin({
|
||||||
|
id:"dingtalk-qr-container",
|
||||||
|
goto: encodeURIComponent(url),
|
||||||
|
style: "border:none;background-color:#FFFFFF;",
|
||||||
|
width : "338",
|
||||||
|
height: "300"
|
||||||
|
});
|
||||||
|
var handleMessage = function (event) {
|
||||||
|
var origin = event.origin;
|
||||||
|
if( origin == "https://login.dingtalk.com" ) { //判断是否来自ddLogin扫码事件。
|
||||||
|
layer.load(1, { shade: [0.1, '#fff'] })
|
||||||
|
var loginTmpCode = event.data;
|
||||||
|
//获取到loginTmpCode后就可以在这里构造跳转链接进行跳转了
|
||||||
|
console.log("loginTmpCode", loginTmpCode);
|
||||||
|
url = url + "&loginTmpCode=" + loginTmpCode
|
||||||
|
window.location = url
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (typeof window.addEventListener != 'undefined') {
|
||||||
|
window.addEventListener('message', handleMessage, false);
|
||||||
|
} else if (typeof window.attachEvent != 'undefined') {
|
||||||
|
window.attachEvent('onmessage', handleMessage);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function () {
|
$(function () {
|
||||||
$("#account,#password,#code").on('focus', function () {
|
$("#account,#password,#code").on('focus', function () {
|
||||||
|
@ -140,6 +177,16 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#btn-dingtalk-qr").on('click', function(){
|
||||||
|
$('form').hide()
|
||||||
|
$(".dingtalk-container").show()
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".btn-dingtalk").on('click', function(){
|
||||||
|
$('form').show()
|
||||||
|
$(".dingtalk-container").hide()
|
||||||
|
})
|
||||||
|
|
||||||
$("#btn-login").on('click', function () {
|
$("#btn-login").on('click', function () {
|
||||||
$(this).tooltip('destroy').parents('.form-group').removeClass('has-error');
|
$(this).tooltip('destroy').parents('.form-group').removeClass('has-error');
|
||||||
var $btn = $(this).button('loading');
|
var $btn = $(this).button('loading');
|
||||||
|
|
Loading…
Reference in New Issue