Commit c498f5e0 by zhujian

'添加a扫码登录'

parent 8fc136ac
......@@ -64,6 +64,8 @@ module.exports = {
appPublic: resolveApp('public'),
appHtml: resolveApp('src/index.html'),
appIndexJs: resolveModule(resolveApp, 'src/index'),
h5Html: resolveApp('src/h5.html'),
h5IndexJs: resolveModule(resolveApp, 'src/h5'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
appTsConfig: resolveApp('tsconfig.json'),
......
<!--
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: wufan
* @LastEditTime: 2021-01-09 11:18:27
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="icon" href="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<!-- <link rel="apple-touch-icon" href="../src/common/images/logo.png" /> -->
<link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/KGSYFEpcHT.png">
<title>小麦企训</title>
<script type="text/javascript" charset="utf-8" src="./jquery.min.js"></script>
</head>
<body>
</body>
<script>
$(document).ready(function () {
var BASIC_HOST_MAP = {
dev: 'https://dev-heimdall.xiaomai5.com/',
dev1: 'https://dev1-heimdall.xiaomai5.com/',
rc: 'https://rc-heimdall.xiaomai5.com/',
gray: 'https://gray-heimdall.xiaomai5.com/',
prod: 'https://gateway-heimdall.xiaomai5.com/'
};
function getParameterByName(name) {
name = name.replace(/[\\[]/, '\\[').replace(/[\]]/, '\\]')
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)')
const results = regex.exec(window.location.href)
return results === null
? ''
: decodeURIComponent(results[1].replace(/\+/g, ' '))
}
const env = getParameterByName('env');
const appTermEnum = getParameterByName('appTermEnum');
const code = getParameterByName('code');
const ticket = getParameterByName('ticket');
console.log(appTermEnum, code, ticket);
if (!code) {
return
}
$.post(BASIC_HOST_MAP[env] + "anon/hades/wXWorkUserTicketLogin",
{
appTermEnum: appTermEnum,
code: code,
ticket: ticket
},
function (data, status) {
});
});
</script>
</html>
\ No newline at end of file
......@@ -77,6 +77,7 @@ checkBrowsers(paths.appPath, isInteractive)
}
const config = configFactory('development');
console.log(config.entry)
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
const useTypeScript = fs.existsSync(paths.appTsConfig);
......
'use strict';
var extend = require('extend');
var qrcodeAlgObjCache = [];
var QRCodeAlg = require('./qrcodealg');
/**
* 计算矩阵点的前景色
* @param {Obj} config
* @param {Number} config.row 点x坐标
* @param {Number} config.col 点y坐标
* @param {Number} config.count 矩阵大小
* @param {Number} config.options 组件的options
* @return {String}
*/
var getForeGround = function getForeGround(config) {
var options = config.options;
if (options.pdground && (config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5 || config.row > config.count - 6 && config.row < config.count - 2 && config.col > 1 && config.col < 5 || config.row > 1 && config.row < 5 && config.col > config.count - 6 && config.col < config.count - 2)) {
return options.pdground;
}
return options.foreground;
};
/**
* 点是否在Position Detection
* @param {row} 矩阵行
* @param {col} 矩阵列
* @param {count} 矩阵大小
* @return {Boolean}
*/
var inPositionDetection = function inPositionDetection(row, col, count) {
if (row < 7 && col < 7 || row > count - 8 && col < 7 || row < 7 && col > count - 8) {
return true;
}
return false;
};
/**
* 二维码构造函数,主要用于绘制
* @param {参数列表} opt 传递参数
* @return {}
*/
var qrcode = function qrcode(opt) {
if (typeof opt === 'string') {
// 只编码ASCII字符串
opt = {
text: opt
};
}
//设置默认参数
this.options = extend({}, {
text: '',
render: '',
size: 256,
correctLevel: 3,
background: '#ffffff',
foreground: '#000000',
image: '',
imageSize: 30
}, opt);
//使用QRCodeAlg创建二维码结构
var qrCodeAlg = null;
for (var i = 0, l = qrcodeAlgObjCache.length; i < l; i++) {
if (qrcodeAlgObjCache[i].text == this.options.text && qrcodeAlgObjCache[i].text.correctLevel == this.options.correctLevel) {
qrCodeAlg = qrcodeAlgObjCache[i].obj;
break;
}
}
if (i == l) {
qrCodeAlg = new QRCodeAlg(this.options.text, this.options.correctLevel);
qrcodeAlgObjCache.push({ text: this.options.text, correctLevel: this.options.correctLevel, obj: qrCodeAlg });
}
if (this.options.render) {
switch (this.options.render) {
case 'canvas':
return this.createCanvas(qrCodeAlg);
case 'table':
return this.createTable(qrCodeAlg);
case 'svg':
return this.createSVG(qrCodeAlg);
default:
return this.createDefault(qrCodeAlg);
}
}
return this.createDefault(qrCodeAlg);
};
extend(qrcode.prototype, {
// default create canvas -> svg -> table
createDefault: function createDefault(qrCodeAlg) {
var canvas = document.createElement('canvas');
if (canvas.getContext) {
return this.createCanvas(qrCodeAlg);
}
var SVG_NS = 'http://www.w3.org/2000/svg';
if (!!document.createElementNS && !!document.createElementNS(SVG_NS, 'svg').createSVGRect) {
return this.createSVG(qrCodeAlg);
}
return this.createTable(qrCodeAlg);
},
// canvas create
createCanvas: function createCanvas(qrCodeAlg) {
var options = this.options;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var count = qrCodeAlg.getModuleCount();
// preload img
var loadImage = function loadImage(url, callback) {
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
img.onload = function () {
callback(this);
img.onload = null;
};
};
//计算每个点的长宽
var tileW = (options.size / count).toPrecision(4);
var tileH = (options.size / count).toPrecision(4);
canvas.width = options.size;
canvas.height = options.size;
//绘制
for (var row = 0; row < count; row++) {
for (var col = 0; col < count; col++) {
var w = Math.ceil((col + 1) * tileW) - Math.floor(col * tileW);
var h = Math.ceil((row + 1) * tileW) - Math.floor(row * tileW);
var foreground = getForeGround({
row: row,
col: col,
count: count,
options: options
});
ctx.fillStyle = qrCodeAlg.modules[row][col] ? foreground : options.background;
ctx.fillRect(Math.round(col * tileW), Math.round(row * tileH), w, h);
}
}
if (options.image) {
loadImage(options.image, function (img) {
var x = ((options.size - options.imageSize) / 2).toFixed(2);
var y = ((options.size - options.imageSize) / 2).toFixed(2);
ctx.drawImage(img, x, y, options.imageSize, options.imageSize);
});
}
return canvas;
},
// table create
createTable: function createTable(qrCodeAlg) {
var options = this.options;
var count = qrCodeAlg.getModuleCount();
// 计算每个节点的长宽;取整,防止点之间出现分离
var tileW = Math.floor(options.size / count);
var tileH = Math.floor(options.size / count);
if (tileW <= 0) {
tileW = count < 80 ? 2 : 1;
}
if (tileH <= 0) {
tileH = count < 80 ? 2 : 1;
}
//创建table节点
//重算码大小
var s = [];
s.push('<table style="border:0px; margin:0px; padding:0px; border-collapse:collapse; background-color:' + options.background + ';">');
// 绘制二维码
for (var row = 0; row < count; row++) {
s.push('<tr style="border:0px; margin:0px; padding:0px; height:' + tileH + 'px">');
for (var col = 0; col < count; col++) {
var foreground = getForeGround({
row: row,
col: col,
count: count,
options: options
});
if (qrCodeAlg.modules[row][col]) {
s.push('<td style="border:0px; margin:0px; padding:0px; width:' + tileW + 'px; background-color:' + foreground + '"></td>');
} else {
s.push('<td style="border:0px; margin:0px; padding:0px; width:' + tileW + 'px; background-color:' + options.background + '"></td>');
}
}
s.push('</tr>');
}
s.push('</table>');
if (options.image) {
// 计算表格的总大小
var width = tileW * count;
var height = tileH * count;
var x = ((width - options.imageSize) / 2).toFixed(2);
var y = ((height - options.imageSize) / 2).toFixed(2);
s.unshift('<div style=\'position:relative; \n width:' + width + 'px; \n height:' + height + 'px;\'>');
s.push('<img src=\'' + options.image + '\' \n width=\'' + options.imageSize + '\' \n height=\'' + options.imageSize + '\' \n style=\'position:absolute;left:' + x + 'px; top:' + y + 'px;\'>');
s.push('</div>');
}
var span = document.createElement('span');
span.innerHTML = s.join('');
return span.firstChild;
},
// create svg
createSVG: function createSVG(qrCodeAlg) {
var options = this.options;
var count = qrCodeAlg.getModuleCount();
var scale = count / options.size;
// create svg
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', options.size);
svg.setAttribute('height', options.size);
svg.setAttribute('viewBox', '0 0 ' + count + ' ' + count);
for (var row = 0; row < count; row++) {
for (var col = 0; col < count; col++) {
var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
var foreground = getForeGround({
row: row,
col: col,
count: count,
options: options
});
rect.setAttribute('x', col);
rect.setAttribute('y', row);
rect.setAttribute('width', 1);
rect.setAttribute('height', 1);
rect.setAttribute('stroke-width', 0);
if (qrCodeAlg.modules[row][col]) {
rect.setAttribute('fill', foreground);
} else {
rect.setAttribute('fill', options.background);
}
svg.appendChild(rect);
}
}
// create image
if (options.image) {
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', options.image);
img.setAttribute('x', ((count - options.imageSize * scale) / 2).toFixed(2));
img.setAttribute('y', ((count - options.imageSize * scale) / 2).toFixed(2));
img.setAttribute('width', options.imageSize * scale);
img.setAttribute('height', options.imageSize * scale);
svg.appendChild(img);
}
return svg;
}
});
module.exports = qrcode;
\ No newline at end of file
<!--
* @Author: 吴文洁
* @Date: 2020-08-24 12:20:57
* @LastEditors: wufan
* @LastEditTime: 2021-01-09 11:18:27
* @Description:
* @Copyright: 杭州杰竞科技有限公司 版权所有
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="icon" href="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!-- <link rel="apple-touch-icon" href="../src/common/images/logo.png" /> -->
<link rel="shortcut icon" href="https://image.xiaomaiketang.com/xm/KGSYFEpcHT.png">
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2223403_qb6r10go4s.css">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>小麦云课堂</title>
<script type="text/javascript" charset="utf-8" src="//g.alicdn.com/sd/ncpc/nc.js?t=2015052012"></script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
import React, { useEffect, useState } from "react";
import ReactDOM from 'react-dom';
// import '@/core/function';
// import Service from "@/common/js/service";
// declare var getParameterByName: any;
function mount() {
ReactDOM.render(
<Main />,
document.getElementById('root'));
}
mount()
function Main() {
return <div>212121</div>
}
......@@ -18,6 +18,10 @@ import 'antd/dist/antd.less';
import 'video-react/dist/video-react.css';
import '@/common/less/index.less';
import '@/core/function';
import Service from "@/common/js/service";
declare var getParameterByName: any;
const history = createHashHistory();
......@@ -25,23 +29,33 @@ window.RCHistory = _.extend({}, history, {
push: (obj: any) => {
history.push(obj)
},
pushState: (obj: any) => {
pushState: (obj: any) => {
history.push(obj)
},
pushStateWithStatus: (obj: any) => {
history.push(obj)
},
goBack: history.goBack,
location: history.location,
replace: (obj: any) => {
history.replace(obj)
}
location: history.location,
replace: (obj: any) => {
history.replace(obj)
}
});
export async function mount() {
function mount() {
ReactDOM.render(
<RootRouter/>,
document.getElementById('root'));
<RootRouter />,
document.getElementById('root'));
}
if (getParameterByName('code')) {
Service.Hades('anon/hades/wXWorkUserLogin', {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
code: getParameterByName('code')
}).then((res) => {
mount()
})
} else {
mount()
}
mount()
\ No newline at end of file
import React, { useEffect, useState} from 'react';
import React, { useEffect, useState } from 'react';
import {
withRouter
} from 'react-router-dom';
import './Login.less';
import {Input,Popover,message} from 'antd';
import { Input, Popover, message, Tabs } from 'antd';
import CheckBeforeSendCode from '../../components/CheckBeforeSendCode';
import User from '@/common/js/user';
import WechatLogin from './WechatLogin'
import BaseService from "@/domains/basic-domain/baseService";
import axios from 'axios';
import axios from 'axios';
import _ from 'underscore';
const { TabPane } = Tabs;
function Login(props) {
const [phone, setPhone] = useState(''); // 登录手机号
......@@ -17,10 +20,10 @@ function Login(props) {
const [checking1, setChecking1] = useState(false);
const [codeText, setCodeText] = useState('获取验证码'); // 验证码提示语
const [waitStatus, setWaitStatus] = useState(false); // 验证码是否在倒计时
const [errorMessage,setErrorMessage] = useState('');
const [phoneError,setPhoneError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [phoneError, setPhoneError] = useState(false);
const [checkObject1, setCheckObject1] = useState({});
async function checkAccount(code, callback = () => { }) {
callback();
}
......@@ -48,7 +51,7 @@ function Login(props) {
token: checkData.token,
scene: 'nc_login',
serverType: "CLOUD_CLASS_LOGIN",
appTermEnum:'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN'
}
BaseService.sendLoginAuthCode(params).then((res) => {
if (!res.success) {
......@@ -86,14 +89,14 @@ function Login(props) {
setErrorMessage("请输入11位手机号")
return;
}
if(!phoneverify){
if (!phoneverify) {
setErrorMessage("请输入验证码");
return;
}
const params = {
phone,
authCode:phoneverify,
appTermEnum:"XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN"
authCode: phoneverify,
appTermEnum: "XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN"
}
BaseService.login(params).then((res) => {
if (!res.success) {
......@@ -112,44 +115,46 @@ function Login(props) {
<div className="login-page" >
<div className="login-main">
<div className="left-banner">
<div><img src={require("../../common/images/logo.png")} alt="" style={{ width: 60,height:61}} /></div>
<div><img src={require("../../common/images/logo.png")} alt="" style={{ width: 60, height: 61 }} /></div>
<div className="name">小麦云课堂</div>
<div className="desc">一键开启直播授课 让知识更有价值</div>
</div>
<div className="login-box">
<div className="login">
<div className="r">
<div className="title">
手机号登录
</div>
<div className="login-form">
<div className="form">
<div className="username" style={{ marginBottom: 16 }}>
<Input
type="phone"
autoComplete="off"
name="account"
maxLength={11}
placeholder="手机号"
value={phone}
onChange={(e) => { setPhone(e.target.value) }}
/>
</div>
<div className="error-message">
</div>
<div className="phoneverify">
<Input
type="text"
id="phoneverify"
name="phoneverify"
placeholder="验证码"
autoComplete="off"
value={phoneverify}
maxLength={4}
onChange={(e) => { setPhoneverify(e.target.value) }}
/>
<Popover
<div className="login">
<div className="r">
<Tabs defaultActiveKey="1" >
<TabPane tab="企业微信登录" key="1">
<WechatLogin></WechatLogin>
</TabPane>
<TabPane tab="手机号登录" key="2">
<div className="login-form">
<div className="form">
<div className="username" style={{ marginBottom: 16 }}>
<Input
type="phone"
autoComplete="off"
name="account"
maxLength={11}
placeholder="手机号"
value={phone}
onChange={(e) => { setPhone(e.target.value) }}
/>
</div>
<div className="error-message">
</div>
<div className="phoneverify">
<Input
type="text"
id="phoneverify"
name="phoneverify"
placeholder="验证码"
autoComplete="off"
value={phoneverify}
maxLength={4}
onChange={(e) => { setPhoneverify(e.target.value) }}
/>
<Popover
visible={openCheck1}
trigger="click"
title=""
......@@ -183,19 +188,23 @@ function Login(props) {
}}
>{codeText}</div>
</Popover>
</div>
<div className="error-message">
{errorMessage}
</div>
<div className="submit">
<div className="btn">
<button id='loginIn' onClick={ () => { handleSubmit() } } >登录</button>
</div>
<div className="error-message">
{errorMessage}
</div>
<div className="submit">
<div className="btn">
<button id='loginIn' onClick={() => { handleSubmit() }} >登录</button>
</div>
</div>
</div>
</div>
</div>
</div>
</TabPane>
</Tabs>
</div>
</div>
</div>
</div>
</div>
......
......@@ -78,6 +78,32 @@
overflow: hidden;
background-color: #ffffff;
border-radius: 4px; //box-shadow: 0 0 17px @sun;
.ant-tabs-tab-btn{
color: #999999;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
line-height: 25px;
&.ant-tabs-tab-active{
color: #333333;
}
}
.ant-tabs-nav::before{
display: none;
}
.ant-tabs-tab{
width: 105px;
text-align: center;
}
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list{
margin: 0 auto;
}
.ant-tabs-top > .ant-tabs-nav .ant-tabs-ink-bar{
width: 24px !important;
height: 4px;
margin-left: 30px;
}
.l {
width: 280px;
height: 100%;
......@@ -420,4 +446,5 @@
}
}
}
}
\ No newline at end of file
.wechatLoginBox{
height: 320px;
text-align: center;
.text{
margin-top: 12px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 20px;
}
.rwm{
position: relative;
width: 160px;
height: 160px;
text-align: center;
display: inline-block;
margin-top: 24px;
.error{
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.95);
display: flex;
align-items:center;
justify-content:center;
left:0px;
top: 0px;
div{
margin: 0 10px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 20px;
}
.ope{
cursor: pointer;
color:rgba(82, 137, 250, 1);
}
}
}
.ant-tabs-tab-active{
.ant-tabs-tab-btn{
color: #333333;
}
}
.ant-tabs-tab-btn{
color: #999999;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
line-height: 25px;
}
.ant-tabs-nav::before{
display: none;
}
.ant-tabs-tab{
width: 105px;
text-align: center;
}
.ant-tabs > .ant-tabs-nav .ant-tabs-nav-list{
margin: 0 auto;
}
.ant-tabs-top > .ant-tabs-nav .ant-tabs-ink-bar{
width: 24px !important;
height: 4px;
margin-left: 30px;
}
}
import React, { useState, useRef, useEffect } from 'react';
import qrcode from '@/core/qrcode/qrcode.js'
import Service from "@/common/js/service";
import './WechatLogin.less'
const Logo = require("@/common/images/logo.png")
export default function WechatLogin(props: any) {
const freshTime = 60;
const init: any = null;
const [status, setStatus] = useState(0);
const [ticket, setTicket] = useState('');
const [leftTime, setLeftTime] = useState(freshTime)
const QRCode = useRef(init);
const timer = useRef(init);
const leftTimeRef = useRef(init);
useEffect(() => {
leftTimeRef.current = leftTime;
}, [leftTime])
useEffect(() => {
clearInterval(timer.current as any);
if (status === 0) {
Service.Hades("anon/hades/getTicket", {}).then((res: any) => {
setTicket(res.result)
let qrnode = new qrcode({
text: res.result,
correctLevel: 2,
size: 160,
image: Logo,
imageSize: 50
});
QRCode.current.innerHTML = ''
QRCode.current.prepend(qrnode);
setLeftTime(freshTime);
timer.current = setInterval(() => {
if (leftTime == 0) {
clearInterval(timer.current);
setStatus(1);
return
}
setLeftTime(leftTimeRef.current - 1);
}, 1000)
})
}
return () => {
clearInterval(timer.current);
}
}, [status])
useEffect(() => {
if (leftTime == 60) {
return
}
return
Service.Hades('anon/hades/wXWorkUserLogin', {
appTermEnum: 'XIAOMAI_CLOUD_CLASS_PC_WEB_ADMIN',
code: ticket
})
}, [leftTime])
return <div className='wechatLoginBox'>
<div className="rwm">
<div id="qrcode" ref={(dom) => {
QRCode.current = dom
}}></div>
{
status === 1 && <div className="error">
<div>二维码已过期
<p className="ope" onClick={() => {
setStatus(0)
}}>刷新</p>
</div>
</div>
}
{
status === 2 && <div className="error">
<div>所在企业还未注册店铺
<p className="ope" onClick={() => {
setStatus(0)
}}>我知道了</p>
</div>
</div>
}
{
status === 3 && <div className="error">
<div>你还不是店铺员工,请联系企业管理员
<p className="ope" onClick={() => {
setStatus(0)
}}>我知道了</p>
</div>
</div>
}
</div>
<p className='text'>请使用企业微信扫码登录</p>
</div>
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment