# loginPage - 登录页面扩展

为系统提供一个新的登录页面模版,登录页面扩展可以在系统设置-登录页面设置中被选用,只有被选用后,登录页面扩展产生的登录页面才会真正生效。

登录页面扩展的机制就是提供一个模版给系统设置-登录页面设置功能,并根据设置页面中用户的设置动态的生产一个HTML文件,最后将HTML存入元数据系统项目的指定位置(/sysdata/public/login/index.html)后登录页面就正式生效了。

# 扩展文件结构

/
├──package.json //定义扩展的配置信息
├──main.ts //扩展的主体代码,提供动态生产HTML文件内容的逻辑
├──thumbnail.png //缩略图
├──thumbnail-phone.png //系统设置中,选择登录页面模版时当用户切换到手机时使用
├──index.html //必须存在,可以使用响应式布局兼容多种设备
├──index.less //样式写这里
├──index.ts //默认不存在
└──images/ //图片存放目录

# package.json

"contributes": {
    "loginPage": {
        /**
         * 是否支持手机
         */
		"supportPhone":boolean,
		
		"properties":{

		}
    }
}

# index.html

index.html就是一个常规的html页面,开发者可以按需要自由进行编写,需要遵守一些约定,如需要存在一些约定了id或class的元素。

# 系统自动注入到登录页面的内容

系统初始化一个登录页面时会先在后端加载 index.html内容并解析,然后自动注入一些内容到html页面后再发送给前端浏览器,注入的内容包括:

<title>xxx</title>
<meta itemprop="name" content="xxxx">
<meta name="description" itemprop="description" content="xxxx">
<meta name="succ-context-path" content="">

...

<link rel="stylesheet" href="/dist/sys/sys-all.css" type="text/css">
<link rel="stylesheet" href="/dist/commons/icons.css" type="text/css">

...

<script type="text/javascript" src="/dist/sys/sys-all.js"></script>
<script type="text/javascript" src="/dist/i18n/zh_CN.js"></script>
<script type="text/javascript" src="/dist/security/login.js"></script>
<script sz-template="html">
requirejs(['sys/sys'], function(sys) {
    sys.ready('security/login').then(function(m){
        m.initLoginPage();
})});
</script>

# 登录页面的典型DOM结构

开发者在开发自己的登录页面时可以不用自己在html中写上面自动注入的内容,一个典型的登录页面的结构如下:

<!DOCTYPE html>

<head>
	<meta charset="utf-8">
    <style type="text/css">
        ...
    </style>
</head>
<body>
    <div id="page" class="login-body loading">						<!-- page 页面顶层dom -->
    	<div class="version"></div>                                 <!-- 显示产品版本信息的元素 -->
    	<div class="logo"></div>
    	<div class="login-container">								<!-- 登录框 -->
    		<div class="type-switcher">...</div>	 				<!-- 登录内容切换器 -->
    		<div class="login-content"> 							<!-- 登录内容 -->
    			<div class="password-content">...</div>				<!-- 账号密码输入框 -->
    			<div class="qrcode-container">						<!-- 二维码框, 只有系统中配置对应的二维码登录方式的时候才会即时创建 -->
    				<div class="qrcode-title"></div>
    				<div class="qrcode-content">
    					<div class="qrcode qrcode-wechat"></div>
    					<div class="qrcode qrcode-wework"></div>
    				</div>
    				<div class="qrcode-btns">						<!-- 只有有多种二维码登录方式的时候才会创建 -->
    					<span class="btn-wechat qtcode-btn"></span>
    					<span class="btn-wework qtcode-btn"></span>
    				</div>
    			</div>
    		</div>
    	</div>
    </div>
</body>

</html>

# 登录页面前端初始化流程

前端浏览器渲染登录页面时会进行如下步骤:

  • 先使用ajax请求获得系统登录页面的一些基本配置信息,例如是否启用了记住密码功能
  • 根据系统设置信息自动进行一些页面内部的元素的class的调整(具体见下文)
  • 如果存在全局js变量LoginPageCustomJS,那么会自动调用它内部定义的一些约定方法(具体见下文)

页面上用户进行了某些操作或系统某些选项打开后page(即id为page的页面元素)的class会自动的进行修改,方便开发者调整页面样式:

  • loading 当前页面正在加载,加载完成后此class会被删除
  • multiple-login 当前页面支持多种登录方式,比如用户启用了微信扫码登录
  • password 当前显示的是用户名密码登录方式
  • qrcode 当前切换到了二维码扫码登录方式
  • qrcode-wework 当前切换到了企业微信扫码登录方式
  • qrcode-wechat 当前切换到了微信扫码登录方式
  • rememberme 系统启用了记住密码功能
  • captcha 当前需要输入验证码

# 登录页面前端事件接口

如果存在全局js变量LoginPageCustomJS,那么会自动调用它内部定义的一些约定方法:

点击查看
/**
 * 登录和权限判断相关的前端二次开发脚本接口。
 */
export interface ILoginPageCustomJS {

	/**
	 * 登录页面(与登录对话框不同,登录页面是占满整个浏览器页面的,等会对话框时在页面进行操作时提示用户需要登录时显示
	 * 的一个对话框)初始化后调用,方便二次开发时对登录页面进行一些个性化处理。
	 * 
	 * @param options 登录界面可能会用到的一些参数
	 * @param login 登录页面对象
	 * @returns 可返回空,如果返回的延迟对象,那么resolve后登录页面才显示。
	 */
	onDidLoginPageInit?(options: LoginPageConf, loginPage: LoginPage): Promise<any> | any;

	/**
	 * 登录对话框(与登录页面不同,登录页面是占满整个浏览器页面的,登录对话框时在页面进行操作时提示用户需要登录时显示
	 * 的一个对话框)初始化后调用,方便二次开发时对登录对话框进行一些个性化处理。
	 * 
	 * @param options 登录界面可能会用到的一些参数
	 * @param loginPage 登录对话框对象
	 * @returns 可返回空,如果返回的延迟对象,那么resolve后登录页面才显示。
	 */
	onDidLoginDialogInit?(options: LoginPageConf, loginDialog: LoginDialog): Promise<any> | any;

	/**
	 * 点击登录按钮,提交登录请求前调用
	 * 
	 * @param args.user userId
	 * @param args.password 密码
	 * @param args.remember 是否需要记住密码
	 * @param args.captchaId 验证码id
	 * @param args.captcha 输入的验证码
	 * @returns 明确返回false或者Promise<false> 表明终止提交登录请求
	 */
	onSendLoginRequest?(args: { user: string; password: string; remember?: string, captchaId?: string, catpcha?: string }): Promise<boolean> | boolean | any;

	/**
	 * 完成登录请求之后调用
	 * 
	 * @param result 登录请求响应结果
	 * @param comp 账号密码登录框对象 或者登录对话框对象
	 * @returns 明确返回false或者Promise<false>,表明终止默认请求响应处理
	 */
	onDidSendLoginRequest?(result: JSONObject, comp: LoginDialog | UserLoginPage): Promise<boolean> | boolean | any;

	/**
	 * 当界面出现了需要登录的异常时调用(如匿名用户在访问了某些需要有权限才能访问的页面时),此调用会在默认的登录对话框弹出前
	 * 调用,开发者可以通过此函数实现个性化的提示逻辑。
	 * 
	 * @param e 错误信息, 
	 * @param e.properties.loginTime 需要登录的原因可能是因为系统限制了一个账号只能在一台机器上登录,如果当前登录的账号在其他机器上重新登录,就会将当前的登录挤掉,
	 * 					这个参数就是为了告诉用户挤掉当前登录账号的登录操作时间。
	 * @param e.properties.loginIpAddress 挤掉当前登录账号的登录IP地址
	 * @returns 根据不同的返回结果会有不同的后续执行逻辑:
	 * 	1. true或者undefined 继续执行默认的登录逻辑,弹出登录对话框
	 *  2. false 表示开发者已经自己处理了此异常,系统将不再弹出默认的登录对话框了,并直接reject之前用户发起的业务逻辑
	 * 	3. 其他返回值表明开发者自己完成了登录,系统将继续完成用户所做的业务,
	 */
	onNeedLoginError?(e?: Error): Promise<boolean> | boolean | any;

	/**
	 * 当界面出现了无权限的异常时调用(如用户访问了某些没有权限的页面,匿名用户不会出现此异常,匿名用户只会出现需要登录的异常),此调用
	 * 会在弹出系统默认的权限异常对话框前调用,方便开发者个性化提示信息。
	 * 
	 * @param e 错误信息
	 * @returns 根据不同的返回结果会有不同的后续执行逻辑:
	 * 	1. false 表示开发者已经自己处理了此异常,系统将不再弹出默认的提示框
	 * 	2. true或其他值,系统将继续弹出默认提示狂
	 */
	onPermissionError?(e: Error,): Promise<boolean> | boolean | any;

	/**
	 * 提交登录请求报错时候调用
	 * 
	 * @param e 错误信息
	 * @param userLoginPage 账号密码登录框对象
	 * @returns 如果返回 false, 则结束登录请求操作
	 */
	onLoginError?(e: Error, userLoginPage: UserLoginPage | LoginDialog): Promise<boolean> | boolean | any;
}

# 兼容移动设备

登录页面模版应该总是兼容移动设备,2个方法(推荐方法1):

  1. 使用响应式布局,让index.html同时支持多种设备
  2. 单独实现index-phone.htmlindex-pad.html

注意,如果用户设置了登录页面的自定义js或css,那么移动端和PC端都将自动加载。

# 示例

下面是系统默认登录页面的html代码:

点击查看
<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title>SuccBI</title>
	<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
	<style type="text/css">
	/*--------------------------------------根据屏幕宽度设置基准 START------------------------------------------------- */
	@media only screen and (min-width: 400px) {
		html {
			font-size: 8px !important;
		}
	}

	@media only screen and (min-width: 1500px) {
		html {
			font-size: 9px !important;
		}
	}

	@media only screen and (min-width: 1800px) {
		html {
			font-size: 10px !important;
		}
	}

	@media only screen and (min-width: 2100px) {
		html {
			font-size: 11px !important;
		}
	}

	@media only screen and (min-width: 2400px) {
		html {
			font-size: 12px !important;
		}
	}

	@media only screen and (min-width: 2700px) {
		html {
			font-size: 13px !important;
		}
	}

	@media only screen and (min-width: 3000px) {
		html {
			font-size: 14px !important;
		}
	}

	@media only screen and (min-width: 3300px) {
		html {
			font-size: 15px !important;
		}
	}

	@media only screen and (min-width: 3600px) {
		html {
			font-size: 16px !important;
		}
	}

	@media only screen and (min-width: 3900px) {
		html {
			font-size: 17px !important;
		}
	}

	/*--------------------------------------根据屏幕自适应 END------------------------------------------------- */

	html,
	body {
		overflow: hidden;
		width: 100%;
		height: 100%;
		font-size: 12px;
		font-family: 'Helvetica Neue', Helvetica, Arial, "Hiragino Sans GB", "Microsoft YaHei";
		color: #333;
		-webkit-text-size-adjust: none;
		margin: 0;
		background: #fff;
		padding: 0;
	}

	.page {
		width: 100%;
		height: 100%;
		background: no-repeat center url(images/login-bg.jpg);
		background-position: center center;
		background-size: cover;
		background-repeat: no-repeat;
	}

	.loading,
	.invisible {
		display: none;
	}

	/** 加载对话框的时候,可以先展示page背景图,避免界面闪动 */
	.loading.page {
		display: block;
	}

	.loading .main-container {
		display: block;
	}

	.multiple-login .type-switcher,
	.captcha-show .row-captcha,
	.rememberme-show .login-remember {
		display: block;
	}

	.header {
		text-align: center;
		height: 7rem;
		display: flex;
		flex-direction: row;
		justify-content: center;
		align-items: center;
	}

	.logo {
		width: 10rem;
		font-size: 1.8rem;
		line-height: 4rem;
		height: 4.8rem;
		background: url(images/logo.svg);
		background-position: center;
		background-size: contain;
		background-repeat: no-repeat;
	}

	.logo-caption {
		font-size: 20px;
		color: #333333;
		height: 60px;
		line-height: 60px;
		padding-left: 8px;
	}

	.logo-caption-phone {
		display: none;
		font-size: 20px;
		color: #333333;
		height: 60px;
		line-height: 60px;
		padding-left: 8px;
	}

	.login-content {
		position: absolute;
		top: 50%;
		left: 70%;
		margin: -18rem 0 0 -24rem;
		text-align: center;
		width: 34rem;
		min-height: 36rem;
		display: flex;
		flex-direction: column;
		background-image: linear-gradient(0deg, rgba(248, 250, 252, 0.62) 1%, rgba(251, 252, 253, 0.90) 47%, rgba(255, 255, 255, 0.35) 97%);
		box-shadow: 0 0 34px 0 rgba(113, 188, 247, 0.14), 0 0 64px 0 rgba(113, 188, 247, 0.27);
	}

	.login-box-body {
		padding: 0 12%;
	}

	.login-box-header {
		height: 3.5rem;
		display: none;
	}

	.show-user-tabs.login-box-header {
		display: block;
	}

	.login-tab-header {
		height: 100%;
		padding: 0 12%;
	}

	.header-wrapper {
		display: flex;
		flex-direction: row;
		justify-content: space-around;
		padding-inline-start: 0;
		height: 100%;
	}

	.tab-item {
		list-style-type: none;
		position: relative;
		font-size: 1.6rem;
		color: rgba(0, 0, 0, 0.4);
		letter-spacing: 0.92px;
		cursor: pointer;
	}

	.tab-item:hover {
		opacity: 0.8;
	}

	.inner-active .item-inner,
	.outer-active .item-outer {
		color: #3892D4;
		opacity: 1;
	}

	.login-account,
	.login-qrcode,
	.login-phone {
		display: none;
	}

	.sys .no-sys-account:not(.only-external) .login-account,
	.sys .no-sys-account:not(.only-external) .switch-account {
		display: none;
	}
	.sys .no-sys-qrcode:not(.only-external) .login-qrcode,
	.sys .no-sys-qrcode:not(.only-external) .switch-qrcode {
		display: none;
	}
	.sys .no-sys-phone:not(.only-external) .login-phone,
	.sys .no-sys-phone:not(.only-external) .switch-phone {
		display: none;
	}
	.external .no-external-account:not(.only-sysuser) .login-account,
	.external .no-external-account:not(.only-sysuser) .switch-account {
		display: none;
	}
	.external .no-external-qrcode:not(.only-sysuser) .login-qrcode,
	.external .no-external-qrcode:not(.only-sysuser) .switch-qrcode {
		display: none;
	}
	.external .no-external-phone:not(.only-sysuser) .login-phone,
	.external .no-external-phone:not(.only-sysuser) .switch-phone {
		display: none;
	}
	.account .only-sysuser .login-options > .switch-qrcode,
	.account .only-external .login-options > .switch-qrcode,
	.account .qrcode-switcher,
	.account .login-phone,
	.account .login-qrcode {
		display: none;
	}

	.account .password-switcher,
	.account .login-account {
		display: block;
	}

	.phone .only-sysuser .login-options-phone>.switch-qrcode,
	.phone .only-external .login-options-phone>.switch-qrcode,
	.phone .qrcode-switcher,
	.phone .login-qrcode,
	.phone .login-account {
		display: none;
	}

	.phone .password-switcher,
	.phone .login-phone {
		display: block;
	}

	.qrcode .only-sysuser .login-options-qrcode,
	.qrcode .only-external .login-options-qrcode,
	.qrcode .password-switcher,
	.qrcode .login-phone,
	.qrcode .login-account {
		display: none;
	}

	.qrcode .qrcode-switcher,
	.qrcode .login-qrcode {
		display: block;
	}

	.login-status {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 100%;
		text-align: center;
		color: red;
		height: 2rem;
		margin-bottom: 0.8rem;
		flex: 0.2 1 auto;
	}

	.login-success {
		display: flex;
		align-items: center;
		justify-content: center;
		width: 100%;
		min-height: 30px;
		text-align: center;
	}

	.row-user,
	.row-password,
	.row-captcha,
	.row-code,
	.row-phone {
		position: relative;
		width: 100%;
		flex: 0 1 auto;
		height: 4rem;
		margin-bottom: 2rem;
	}

	.row-captcha {
		text-align: left;
		display: none;
	}

	.captcha-show .row-captcha {
		display: block;
	}

	.row-code {
		display: flex;
		flex-direction: row;
		justify-content: space-between;
	}

	.row-remember {
		display: flex;
		justify-content: space-between;
		margin-bottom: 2rem;
	}

	.row-user:before,
	.row-password:before,
	.row-captcha:before,
	.row-phone:before,
	.row-code:before {
		font-family: "szfont";
		position: absolute;
		left: 0.9rem;
		font-size: 1.4rem;
		color: #475665;
		letter-spacing: 0.117rem;
		z-index: 1;
		/* 避免FireFox自动填充表单时遮挡图标 */
		top: 50%;
		transform: translateY(-50%);
	}

	.row-user:before {
		content: '\e043';
	}

	.row-password:before,
	.row-code:before {
		content: '\e168';
	}

	.row-captcha:before {
		content: '\e152';
	}

	.row-phone:before {
		content: '\e510';
	}

	.input-user,
	.input-password,
	.input-captcha,
	.input-phone,
	.input-code,
	.input-sendcode {
		display: block;
		width: 100%;
		padding: 0 1rem 0 15%;
		font-size: 1.2rem;
		box-sizing: border-box;
		outline: none;
		background: #FFFFFF;
		border: 0 solid #E0F1FF;
		box-shadow: 0 0 16px 0 rgba(96, 165, 255, 0.04), 0 0 38px 0 rgba(88, 134, 181, 0.11);
		border-radius: 0.2rem;
		height: 100%;
	}

	.input-sendcode.waiting {
		color: #d2d2d2;
		pointer-events: none;
		cursor: not-allowed;
	}

	.input-code {
		width: 60%;
		padding-right: 1rem;
	}

	.input-sendcode {
		width: 34%;
		padding: 0;
		color: #369FF1;
		text-align: center;
		cursor: pointer;
	}

	.input-sendcode:hover {
		opacity: 0.8;
	}

	.input-user:focus,
	.input-password:focus,
	.input-captcha:focus,
	.input-phone:focus,
	.input-code:focus {
		border-color: #76BDED;
		box-shadow: 0px 0px 0.3rem 0px #76BDED;
	}

	.input-captcha,
	.input-code {
		display: inline-block;
		vertical-align: top;
		width: 54%;
	}

	.captcha-img,
	.input-sendcode {
		float: right;
		width: 40%;
		padding: 0;
		border: none;
		cursor: pointer;
		border-radius: 0.2rem;
		height: 100%;
	}

	.login-btn {
		background: #3890DD;
		border-radius: 1.77rem;
		width: 100%;
		font-size: 1.6rem;
		color: #fff;
		cursor: pointer;
		user-select: none;
		-webkit-user-select: none;
		-moz-user-select: none;
		-ms-user-select: none;
		flex: 0 1 auto;
		height: 4rem;
		margin-bottom: 2rem;
		display: flex;
		align-items: center;
		justify-content: center;
	}

	.login-btn:hover {
		opacity: 0.9;
	}

	.login-options,
	.login-options-qrcode,
	.login-options-phone {
		display: flex;
		justify-content: space-around;
		width: 100%;
		height: 2rem;
		margin-top: 1.8rem;
		margin-bottom: 1.8rem;
	}

	.login-options-qrcode {
		margin-top: 0;
	}

	.login-option {
		height: 1.7rem;
		line-height: 1.7rem;
		font-size: 1.2rem;
		color: #686F78;
		cursor: pointer;
		width: 50%;
		position: relative;
		user-select: none;
		-webkit-user-select: none;
		-moz-user-select: none;
		-ms-user-select: none;
	}

	.login-option:hover {
		opacity: 0.8;
	}

	.sys>.sys-all:not(.only-sysuser) .switch-qrcode+.switch-phone:before,
	.sys>.sys-all:not(.only-sysuser) .switch-qrcode+.switch-account:before,
	.sys>.sys-all:not(.only-sysuser) .switch-phone+.switch-account:before,
	.external>.external-all:not(.only-external) .switch-qrcode+.switch-phone:before,
	.external>.external-all:not(.only-external) .switch-qrcode+.switch-account:before,
	.external>.external-all:not(.only-external) .switch-phone+.switch-account:before {
		content: " ";
		position: absolute;
		width: 1px;
		height: 1.7rem;
		left: 0;
		top: 0;
		background-color: #ABBBCC;
	}

	.login-remember {
		cursor: pointer;
		color: #3D4146;
	}

	.login-remember:hover {
		opacity: 0.8;
	}

	.login-remember:before {
		content: "\e172";
		color: #FFFFFF;
		font-family: "szfont";
		font-size: 1.6rem;
		margin-right: 0.4rem;
		vertical-align: middle;
		box-shadow: 0 0 16px 0 rgba(96, 165, 255, 0.04), 0 0 28px 0 rgba(54, 97, 140, 0.15);
		border-radius: 0.1rem;
	}

	.remember-checked:before {
		content: "\e173";
		color: #3890DD;
		font-family: "szfont";
		font-size: 1.6rem;
		margin-right: 0.4rem;
		vertical-align: middle;
	}

	.login-type-btn {
		font-family: "szfont";
		font-size: 2.4rem;
		vertical-align: top;
		margin: 0 2rem;
		cursor: pointer;
	}

	.qrcode-content {
		height: 26rem;
		display: flex;
		flex-direction: column;
		align-items: center;
		min-height: 23rem;
		align-content: space-around;
		position: relative;
	}

	.qrcode-content>.qrcode{
		width: 21rem;
		height: 21rem;
		margin-top: 1rem;
	}

	.qrcode-desc {
		padding-top: 2rem;
		font-size: 1rem;
		color: rgba(0, 0, 0, 0.8);
	}

	.qrcode-img { 
		width: 100%;
		height: 100%;
	}

	.version {
		height: 2rem;
		opacity: 0.5;
		font-family: PingFangSC-Regular;
		font-size: 1.2rem;
		color: #FFFFFF;
		position: fixed;
		right: 10px;
		bottom: 6px;
	}

	.login-loading {
		width: 100%;
		height: 30rem;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.loading-ring {
		position: relative;
		width: 5rem;
		height: 5rem;
		box-shadow: inset 0 0 0 5px #DFDFDF;
		border-radius: 50%;
	}

	.loading-circle {
		position: absolute;
		clip: rect(0, 5rem, 5rem, 2.5rem);
		width: 5rem;
		height: 5rem;
		border-radius: 50%;
		box-shadow: inset 0 0 0 5px #1B97F9;
		animation: rotate 0.6s linear infinite;
	}

	.both-user .type-switcher {
		display: none;
	}

	.only-sysuser .type-switcher,
	.only-external .type-switcher {
		display: block;
	}

	.only-external>.no-switcher .type-switcher,
	.only-sysuser>.no-switcher .type-switcher {
		display: none;
	}

	.type-switcher {
		display: none;
		position: absolute;
		height: 6rem;
		width: 6rem;
		right: 0;
		user-select: none;
		cursor: pointer;
	}

	.type-switcher:hover {
		opacity: 0.6;
	}

	.switcher-item {
		width: 100%;
		height: 100%;
	}

	.qrcode-switcher {
		display: none;
		background-size: cover;
		background-repeat: no-repeat;
		background-image: url(images/pc.png);
	}

	.password-switcher {
		display: none;
		background-size: cover;
		background-repeat: no-repeat;
		background-image: url(images/code.png);
	}

	.qrcode-refresh-mask {
		position: absolute;
		background: white;
		opacity: 0.9;
		height: 21.2rem;
		width: 21.2rem;
		margin-top: 0.9rem;
	}

	.qrcode-refresh-content {
		color: #222;
		display: inline-block;
		position: absolute;
		height: 11rem;
		width: 13rem;
		top: 50%;
		margin-top: -3.5rem;
		left: 50%;
		margin-left: -6.5rem;
		text-align: center;
	}

	.qrcode-refresh-btn {
		cursor: pointer;
		background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#F8F8F8));
		background-image: -webkit-linear-gradient(top, #fff 0, #F8F8F8 100%);
		background-image: -o-linear-gradient(top, #fff 0, #F8F8F8 100%);
		background-image: linear-gradient(to bottom, #fff 0, #F8F8F8 100%);
		filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#FFFFFFFF", endColorstr="#FFF8F8F8", GradientType=0);
		border-radius: 2px;
		vertical-align: middle;
		border: 1px solid #AAA;
		display: inline-block;
		margin: 0;
		-webkit-box-sizing: content-box;
		box-sizing: content-box;
		min-width: 24px;
		height: 24px;
		padding: 0 12px;
		background: #F5F5F5;
		line-height: 24px;
		outline: 0;
		text-align: center;
		font-size: 12px;
		color: #222;
		-webkit-user-select: none;
		-moz-user-select: none;
		-ms-user-select: none;
		user-select: none;
		margin-top: 1rem;
		text-decoration: none;
	}

	.foot-bar {
		display: none;
	}

	@media screen and (min-width: 0px) and (max-width: 480px) {

		/** 屏幕宽度小于480px时,只显示登录对话框,方便将登录界面直接嵌入到iframe作为登录窗口使用  */
		.page {
			background-image: none;
			background-size: cover;
		}

		.content,
		.login-box,
		.page-content {
			height: 100%;
		}

		.login-content {
			height: 75%;
			width: 100%;
			position: relative;
			margin: 0;
			top: auto;
			left: auto;
			background-image: none;
			box-shadow: none;
		}

		.header {
			height: 35%;
		}

		.logo {
			height: 100px;
			background: no-repeat center url(images/logo-mobile.svg);
			background-repeat: no-repeat;
			background-position: center;
			margin: auto;
		}

		.login-btn {
			background: #369FF1;
			height: 46px;
			line-height: 46px;
			text-align: center;
			font-size: 17px;
			margin-top: 40px;
			color: #fff;
			border-radius: 4px;
			cursor: pointer;
		}

		.tab-item {
			font-size: 2.4rem;
		}

		.row-user,
		.row-password,
		.row-captcha,
		.row-code,
		.row-phone {
			margin-bottom: 3rem;
			height: 4.5rem;
		}

		.login-remember:before,
		.row-user:before,
		.row-password:before,
		.row-captcha:before,
		.row-phone:before,
		.row-code:before {
			top: 15px;
			left: 9px;
			font-size: 18px;
		}
		
		.input-user,
		.input-password,
		.input-captcha,
		.input-phone,
		.input-code {
			border: none;
			border-bottom: 1px solid #CDCDCD;
			box-shadow: none;
			font-size: 16px;
		}

		.input-user:focus,
		.input-password:focus,
		.input-captcha:focus,
		.input-phone:focus,
		.input-code:focus {
			border-color: #369FF1;
			box-shadow: none;
		}

		.inner-active .item-inner,
		.outer-active .item-outer {
			color: #369FF1;
		}

		.inner-active .item-inner:before,
		.outer-active .item-outer:before {
			background: #369FF1;
		}

		.remember-checked:before {
			color: #369FF1;
		}

		.foot-bar {
			flex-direction: column;
			width: 100%;
			height: 25%;
			justify-content: center;
			align-items: center;
		}

		.foot-bar.show-bar {
			display: flex;
		}

		.foot-title {
			font-size: 12px;
			color: #666666;
			position: relative;
			height: 50px;
			display: flex;
			margin: 0 30px;
			white-space: nowrap;
			align-items: center;
		}

		.foot-title:before {
			content: "";
			display: inline-block;
			margin-right: 20px;
			height: 1px;
			width: 100px;
			border-bottom: 1px solid #e0e0e0;
		}

		.foot-title:after {
			content: "";
			display: inline-block;
			margin-left: 20px;
			height: 1px;
			width: 100px;
			border-bottom: 1px solid #e0e0e0;
		}

		.foot-content {
			display: flex;
			width: 100%;
			justify-content: space-around;
		}

		.logins-item {
			width: 45px;
			height: 45px;
			border-radius: 30px;
			position: relative;
		}

		.invisible.logins-item {
			display: none;
		}

		#login-wxservice {
			background: no-repeat center url(images/wechat.svg);
		}

		#login-wxminiprogram {
			background: no-repeat center url(images/wxapp.svg);
		}

		.only-sysuser .type-switcher {
			display: none;
		}

		.page .version,
		.type-switcher,
		.login-title,
		.login-qrcode,
		.row-remember,
		.switch-qrcode {
			display: none;
		}

		.no-link-switch:not(.one-type) .login-options,
		.no-link-switch:not(.one-type) .login-options-phone {
			display: flex;
		}

		.sys>.sys-all:not(.only-sysuser) .switch-qrcode+.switch-phone:before,
		.sys>.sys-all:not(.only-sysuser) .switch-qrcode+.switch-account:before,
		.external>.external-all .switch-qrcode+.switch-phone:before,
		.external>.external-all .switch-qrcode+.switch-account:before {
			display: none;
		}

		html {
			font-size: 8px !important;
		}
	}

	</style>
</head>

<body>
	<noscript>
		<style>
			.page .loading{
				display: block;
			}
		</style>
	</noscript>
	<div id="basepage" class="page">
		<noscript>
			您的浏览器未启用 JavaScript,您需要启用 JavaScript 才能进行登录
			<br> your browser disabled JavaScript, you need to enable JavaScript before trying to login
		</noscript>
		<div class="page-content">
			<div class="content">
				<div class="login-content">
					<div class="type-switcher" id="switcher">
						<div class="password-switcher switcher-item"></div>
						<div class="qrcode-switcher switcher-item"></div>
					</div>
					<div class="login-box">
						<div class="header">
							<div class="logo"></div>
							<div class="logo-caption"></div>
							<div class="logo-caption-phone">SuccBI</div>
						</div>
						<div class="login-box-header inner-active">
							<div class="login-tab-header">
								<div class="header-wrapper">
									<li class="tab-item item-inner" id="inner" login.page.sysUser>内部用户</li>
									<li class="tab-item item-outer" id="outer" login.page.externalUser>外部用户</li>
								</div>
							</div>
						</div>
						<div class="login-box-body">
							<div class="login-account" id="login-account">
								<div class="login-status" id="status"></div>
								<div class="row-user">
									<input id="user" name="user" placeholder="用户名" type="text" autocapitalize="off" class="input-user" i18n-key="login.page.user" />
								</div>
								<div class="row-password">
									<input id="password" name="password" placeholder="密码" type="password" class="input-password" i18n-key="login.page.password" />
								</div>
								<div class="row-captcha" id="captcha-container">
									<input id="captcha" name="captcha" placeholder="验证码" type="text" class="input-captcha" i18n-key="login.page.captcha">
									<img class="captcha-img" id="captcha-img">
								</div>
								<div class="row-remember">
									<span class="login-remember" id="remember" i18n-key="login.page.remember">记住我的登录状态</span>
								</div>
								<div class="login-btn" id="login-btn-account" i18n-key="login.page.login">&nbsp;&nbsp;</div>
								<div class="login-options">
									<span class="login-option switch-qrcode" id="account-to-qrcode" i18n-key="login.page.qrcodeLogin">扫码登录</span>
									<span class="login-option switch-phone" id="account-to-phone" i18n-key="login.page.smsLogin">手机号登录</span>
								</div>
							</div>
							<div class="login-qrcode" id="login-qrcode">
								<div class="qrcode-body"></div>
								<div class="login-options-qrcode">
									<span class="login-option switch-phone" id="qrcode-to-phone" i18n-key="login.page.smsLogin">手机号登录</span>
									<span class="login-option switch-account" id="qrcode-to-account" i18n-key="login.page.passwordLogin">账号登录</span>
								</div>
							</div>
							<div class="login-phone" id="login-phone">
								<div class="login-status" id="status-phone"></div>
								<div class="row-phone">
									<input id="phone" class="input-phone" name="phone" type="tel" placeholder="请输入手机号" i18n-key="login.page.phone">
								</div>
								<div class="row-captcha" id="captcha-container">
									<input id="phone-captcha" name="captcha" placeholder="验证码" type="text" class="input-captcha" i18n-key="login.page.captcha">
									<img class="captcha-img" id="phone-captcha-img">
								</div>
								<div class="row-code" id="code-container">
									<input id="code" name="code" placeholder="短信验证码" type="text" class="input-code" i18n-key="login.page.code">
									<input id="send-code" name="send-code" type="button" class="input-sendcode" value="获取验证码" i18n-key="login.page.sendcode">
								</div>
								<div class="login-btn" id="login-btn-sms" i18n-key="login.page.login">&nbsp;&nbsp;</div>
								<div class="login-options-phone">
									<span class="login-option switch-qrcode" id="phone-to-qrcode" i18n-key="login.page.qrcodeLogin">扫码登录</span>
									<span class="login-option switch-account" id="phone-to-account" i18n-key="login.page.passwordLogin">账号登录</span>
								</div>
							</div>
						</div>
					</div>
				</div>
				<div class="foot-bar">
					<div class="foot-title" i18n-key="login.page.thirdPartyLogin">第三方登录</div>
					<div class="foot-content" id="third-party-logins">
					</div>
				</div>
				<div class="version"></div>
			</div>
		</div>
	</div>

	<script>
		function initDomByConf(conf) {
			var element = document.getElementsByClassName('login-box')[0];
			var content = document.getElementsByClassName('content')[0];
			if (conf.enableExternalUser) {
				if(!conf.enableSysUserQRCodeLogin && !conf.enableSysUserSMSLogin && !conf.enableSysUserPasswordLogin){
					if (!conf.enableExternalUserQRCodeLogin) {
						content.classList.add('no-switcher');
					}
					if (!conf.enableExternalUserSMSLogin && !conf.enableExternalUserPasswordLogin) {
						content.classList.add('no-switcher');
					}
				}
			} else {
				if (!conf.enableSysUserQRCodeLogin) {
					content.classList.add('no-switcher');
				}
				if (!conf.enableSysUserSMSLogin && !conf.enableSysUserPasswordLogin) {
					content.classList.add('no-switcher');
				}
			}
		}

		function initDomEvent(event, page) {
			var item = event.target || event.srcElement;
			var classes = item.classList;
			if (classes.contains("switcher-item")) {
				var conf = page.loginPageConf;
				if (classes.contains("password-switcher")) {
					t = "qrcode";
				} else {
					if (conf.enableSysUserPasswordLogin) {
						t = "account";
					} else {
						t = "phone";
					}
				}
				page.showPage(t);
			}
		}

		requirejs(['sys/sys'], function(sys) {
			sys.ready('security/login').then(function(login) {
				login.initLoginPageTemplate({
					initDomEvent: initDomEvent,
					initDomByConf: initDomByConf
				});
			});
		});
	</script>
</body>

</html>
是否有帮助?
0条评论
评论