login.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <template>
  2. <div>
  3. <Header />
  4. <form class="form-signin" method="post" >
  5. <h3>业界领先的IAM身份管理和认证产品</h3>
  6. <div class="login-type" :class="isqrCode ? 'activeTwo' : 'activeOne'">
  7. <span @click="switchTab(1)" >
  8. <i><Icon iconClass="user" class="user"></Icon></i>
  9. 账号登录
  10. </span>
  11. <span @click = "switchTab(2)">
  12. <i><Icon iconClass="erweima"></Icon></i>
  13. 扫码登录
  14. </span>
  15. </div>
  16. <template v-if = "!isqrCode">
  17. <div class="username">
  18. <label for="username" class="label">
  19. <Icon iconClass="user" class="erweima"></Icon>
  20. </label>
  21. <input
  22. v-model="form.username"
  23. type="text"
  24. name="username"
  25. id="username"
  26. class="form-control"
  27. placeholder="用户名"
  28. />
  29. </div>
  30. <div class="password">
  31. <label for="inputPassword" class="label">
  32. <Icon iconClass="key"></Icon>
  33. </label>
  34. <input
  35. v-model="form.password"
  36. :type= "passwordType"
  37. name="password"
  38. id="inputPassword"
  39. class="form-control"
  40. placeholder="密码" required/>
  41. <i @click="show" v-show="visible">
  42. <Icon
  43. iconClass="eyes-open"
  44. class="eyes-img"
  45. />
  46. </i>
  47. <i @click="show" v-show="!visible">
  48. <Icon
  49. iconClass="eyes-close"
  50. class="eyes-img"/>
  51. </i>
  52. </div>
  53. <div class="check">
  54. <label for="check" class="label">
  55. <Icon iconClass="lock"/>
  56. </label>
  57. <input
  58. type="text"
  59. id="check"
  60. placeholder="验证码"
  61. class="form-control"
  62. v-model="form.captcha"
  63. />
  64. <div class="code">
  65. <img :src="imageCaptcha" @click="getImageCaptcha" alt=""/>
  66. </div>
  67. </div>
  68. <div class="remember">
  69. <input type="checkbox" id="remember" v-model="rememberMe" />
  70. <label for="remember">记住登录</label>
  71. <router-link to="/passport/forgot">忘记密码</router-link>
  72. </div>
  73. <button type="button" class="btn" @click="login" >登录</button>
  74. <div class="otherLogin">
  75. <span> 其他登录方式 </span>
  76. <template
  77. v-for="prod in providers"
  78. >
  79. <img :src= 'prod.icon' alt="" width="32px" @click="socialauth(prod.provider)">
  80. </template>
  81. </div>
  82. </template>
  83. <div v-if="isqrCode">
  84. <div id="div_qrcodelogin"></div>
  85. </div>
  86. </form>
  87. <Footer/>
  88. </div>
  89. </template>
  90. <script lang="ts" setup>
  91. import { AxiosError,AxiosResponse} from 'axios'
  92. import {api} from "../utils/api"
  93. import {reactive,ref,onBeforeMount, onBeforeUpdate} from "vue"
  94. import CONSTS from "../shared/index"
  95. import Icon from "./Icon.vue"
  96. import {ElLoading,ElMessage} from "element-plus"
  97. import {useRouter} from "vue-router"
  98. import Header from "./Header.vue"
  99. import Footer from "./Footer.vue"
  100. interface Form {
  101. username: string,
  102. password: string,
  103. captcha: string ,
  104. }
  105. let state=""
  106. let captchaType = ""
  107. let loginType="normal"
  108. const router = useRouter()
  109. const form = reactive<Form>({
  110. username:"",
  111. password:"",
  112. captcha:"",
  113. })
  114. const rememberMe = ref<boolean>(false)
  115. const isqrCode =ref<boolean>(false)
  116. let passwordType=ref("password")
  117. let visible=ref(false)
  118. let imageCaptcha= ref<string>("")
  119. let providers = ref<object[]>([])//reactive不可以监视?
  120. let qrScan = ""
  121. const getUrl=(value:string)=>{
  122. let params=window.location.search.substring(1).split("&")
  123. for(let i = 0; i < params.length; i++){
  124. let param=params[i].split('=')
  125. if(param[0]===value){
  126. return param[1]
  127. }
  128. }
  129. return '';
  130. }
  131. const socialauth=(provider:string): void =>{
  132. //clear User
  133. localStorage.setItem('user',JSON.stringify({}))
  134. api.authorize(provider)
  135. .then((res)=>{
  136. // debugger
  137. window.location.href = res.data.data
  138. })
  139. }
  140. const congressLogin=(congress:string)=>{
  141. api.congress({congress:congress})
  142. .then((res: AxiosResponse)=>{
  143. res=res.data
  144. if(res.code !== 0){
  145. console.log(res.msg)
  146. }else{
  147. //设置用户token信息
  148. api.auth(res.data)
  149. api.navigate({})
  150. }
  151. })
  152. .catch((err:AxiosError)=>{
  153. console.log(err.message)
  154. Promise.reject(err)
  155. })
  156. }
  157. onBeforeMount(()=>{
  158. if(getUrl(CONSTS.REDIRECT_URI)!==''){
  159. api.setRedirectUri(getUrl(CONSTS.REDIRECT_URI))
  160. }
  161. if(getUrl(CONSTS.CONGRESS)){
  162. congressLogin(getUrl(CONSTS.CONGRESS))
  163. }
  164. //init socails,state
  165. api.clear()
  166. api.get({remember_me:localStorage.getItem(CONSTS.REMEMBER)})
  167. .then((res:AxiosResponse)=>{
  168. res=res.data
  169. if(res.code != 0){
  170. console.log(res.msg)
  171. }else{
  172. if(res.data.state){
  173. providers.value=res.data.socials.providers
  174. qrScan = res.data.socials.qrScan
  175. state =res.data.state
  176. captchaType = res.data.captcha
  177. if(captchaType !== 'NONE'){
  178. //初始化图像验证码
  179. api.getImageCaptcha({state,captcha:captchaType})
  180. .then((res)=>{
  181. res=res.data
  182. imageCaptcha.value=res.data.image
  183. })
  184. }
  185. }
  186. }
  187. })
  188. .catch((err:AxiosError)=>{
  189. Promise.reject(err)
  190. })
  191. })
  192. const show=()=>{
  193. visible.value=!(visible.value);
  194. if(!visible.value)passwordType.value="password"
  195. else passwordType.value="text"
  196. }
  197. const getImageCaptcha=():void=>{
  198. api.getImageCaptcha({state,captcha:captchaType})
  199. .then((res)=>{
  200. res=res.data
  201. imageCaptcha.value=res.data.image;
  202. })
  203. .catch(err=>Promise.reject(err))
  204. }
  205. const login=()=>{
  206. if(form.username===''|| form.password===''|| form.captcha===''){
  207. return;
  208. }
  209. localStorage.setItem(CONSTS.REMEMBER,rememberMe.value === true?"true":"false")
  210. api.login(
  211. {
  212. authType:loginType,
  213. state,
  214. username:form.username,
  215. password:form.password,
  216. captcha:form.captcha,
  217. mobile:null,
  218. otpCaptcha:null,
  219. remeberMe:rememberMe.value,
  220. })
  221. .then((res:AxiosResponse)=>{
  222. res=res.data
  223. if(res.code!=0){
  224. //ElMessage.error(res.message)
  225. getImageCaptcha()
  226. }else{
  227. //清空路由复用信息?
  228. api.auth(res.data)
  229. api.navigate({})
  230. router.push("/dashboard/home")
  231. }
  232. // res.data.token&&localStorage.setItem('token',res.data.token)
  233. })
  234. .catch((err:AxiosError)=>{
  235. console.log(err.message)
  236. Promise.reject(err)
  237. })
  238. }
  239. const switchTab=(index:number):void=>{
  240. if(index===1){
  241. isqrCode.value = false
  242. }
  243. if(index===2){
  244. isqrCode.value=true
  245. localStorage.setItem('user',JSON.stringify({}))
  246. api.scanqrcode(qrScan)
  247. .then((res:AxiosResponse)=>{
  248. res=res.data
  249. if(res.code === 0){
  250. if(qrScan === 'workweixin'){
  251. qrScanWorkweixin(res.data)
  252. }else if(qrScan === 'dingtalk'){
  253. qrScanDingtalk(res.data)
  254. }else if(qrScan === 'feishu'){
  255. qrScanFeishu(res.data)
  256. }
  257. }
  258. })
  259. }
  260. }
  261. const qrScanWorkweixin=(data: any) => {
  262. //see doc https://developer.work.weixin.qq.com/document/path/91025
  263. // @ts-ignore
  264. let wwLogin = new WwLogin({
  265. id: 'div_qrcodelogin',
  266. appid: data.clientId,
  267. agentid: data.agentId,
  268. redirect_uri: encodeURIComponent(data.redirectUri),
  269. state: data.state,
  270. href: 'data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDI1MHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDI1MHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTpub25lfQ0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30='
  271. });
  272. }
  273. const qrScanFeishu=(data: any)=> {
  274. //see doc https://open.feishu.cn/document/common-capabilities/sso/web-application-sso/qr-sdk-documentation
  275. //remove old div
  276. var qrcodeDiv = document.querySelector('#div_qrcodelogin');
  277. qrcodeDiv?.childNodes.forEach(function (value, index, array) {
  278. qrcodeDiv?.removeChild(value);
  279. });
  280. // @ts-ignore
  281. let fsredirectUri = `https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=${data.clientId}&redirect_uri=${encodeURIComponent(
  282. data.redirectUri
  283. )}&response_type=code&state=${data.state}`;
  284. // @ts-ignore
  285. var redirectUri = fsredirectUri;
  286. // @ts-ignore
  287. let QRLoginObj = QRLogin({
  288. id: 'div_qrcodelogin',
  289. goto: redirectUri,
  290. width: '300',
  291. height: '300',
  292. style: 'border: 0;'
  293. });
  294. }
  295. const qrScanDingtalk=(data: any)=> {
  296. //see doc https://open.dingtalk.com/document/isvapp-server/scan-qr-code-to-log-on-to-third-party-websites
  297. var url = encodeURIComponent(data.redirectUri);
  298. var gotodingtalk = encodeURIComponent(
  299. `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${url}`
  300. );
  301. // @ts-ignore
  302. let ddredirect_uri = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${data.clientId}&response_type=code&scope=snsapi_login&state=${data.state}&redirect_uri=${data.redirectUri}`;
  303. // @ts-ignore
  304. var obj = DDLogin({
  305. id: 'div_qrcodelogin', //这里需要你在自己的页面定义一个HTML标签并设置id,例如<div id="login_container"></div>或<span id="login_container"></span>
  306. goto: gotodingtalk, //请参考注释里的方式
  307. style: 'border:none;background-color:#FFFFFF;',
  308. width: '360',
  309. height: '400'
  310. });
  311. }
  312. </script>
  313. <style lang="less" scoped>
  314. @import "../assets/less/login.less";
  315. </style>