login 文件目录调整

This commit is contained in:
Wang Kejun 2022-08-26 11:44:33 +08:00
parent f3f72be506
commit a6dc4c339c
22 changed files with 894 additions and 206 deletions

View File

@ -5,7 +5,7 @@ var Exception_1 = require("oak-domain/lib/types/Exception");
exports.routers = [
[
Exception_1.OakUnloggedInException, {
router: '/mobile/login',
router: '/login',
}
]
];

2
lib/pages/login/index.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
declare const _default: string | JSX.Element;
export default _default;

125
lib/pages/login/index.js Normal file
View File

@ -0,0 +1,125 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var SEND_KEY = 'captcha:sendAt';
exports.default = OakPage({
path: 'login',
isList: false,
projection: {
id: 1,
mobile: 1,
userId: 1,
},
data: {
mobile: '',
password: '',
captcha: '',
counter: 0,
},
properties: {
onlyCaptcha: Boolean,
onlyPassword: Boolean,
eventLoggedIn: String,
},
formData: function (_a) {
var features = _a.features;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var lastSendAt, now, counter;
var _this = this;
return tslib_1.__generator(this, function (_b) {
lastSendAt = features.localStorage.load(SEND_KEY);
now = Date.now();
counter = 0;
if (typeof lastSendAt === 'number') {
counter = Math.max(60 - Math.ceil((now - lastSendAt) / 1000), 0);
if (counter > 0) {
this.counterHandler = setTimeout(function () { return _this.reRender(); }, 1000);
}
else if (this.counterHandler) {
clearTimeout(this.counterHandler);
this.counterHandler = undefined;
}
}
return [2 /*return*/, {
counter: counter,
}];
});
});
},
methods: {
onInput: function (e) {
var _a;
var _b = this.resolveInput(e), dataset = _b.dataset, value = _b.value;
var attr = dataset.attr;
this.setState((_a = {},
_a[attr] = value,
_a));
},
sendCaptcha: function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var mobile, result, err_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
mobile = this.state.mobile;
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.token.sendCaptcha(mobile)];
case 2:
result = _a.sent();
// 显示返回消息
this.setMessage({
type: 'success',
content: result,
});
this.save(SEND_KEY, Date.now());
this.reRender();
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
this.setMessage({
type: 'error',
content: err_1.message,
});
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
},
loginByMobile: function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var eventLoggedIn, _a, mobile, password, captcha, err_2;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
eventLoggedIn = this.props.eventLoggedIn;
_a = this.state, mobile = _a.mobile, password = _a.password, captcha = _a.captcha;
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.token.loginByMobile(mobile, password, captcha)];
case 2:
_b.sent();
if (eventLoggedIn) {
this.pub(eventLoggedIn);
}
else {
this.navigateBack();
}
return [3 /*break*/, 4];
case 3:
err_2 = _b.sent();
this.setMessage({
type: 'error',
content: err_2.message,
});
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
}
},
});

View File

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "登录",
"usingComponents": {}
}

View File

@ -0,0 +1,17 @@
/** index.wxss **/
@import "../../../config/styles/_base.less";
@import "../../../config/styles/_mixins.less";
.page-body {
height: 100vh;
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
background-color: @bg-color-block;
.safe-area-inset-bottom();
}

View File

@ -0,0 +1,13 @@
<!-- index.wxml -->
<view class="page-body">
<block wx:if="{{mobiles && mobiles.length > 0}}">
<view wx:for="{{mobiles}}" wx:key="index" class="card">
<text>{{item.mobile}}</text>
</view>
</block>
<block wx:else>
<view style="flex:1; display:flex; align-items:center;justify-content:center">尚未授权手机号</view>
</block>
<view style="display: flex; flex: 1"></view>
<t-button theme="primary" style="margin: 16rpx" block size="large" open-type="getPhoneNumber" bindgetphonenumber="onRefreshMobile" content="授权手机号" />
</view>

View File

@ -0,0 +1,13 @@
{
"Log in": "登录",
"Remember me": "记住账号",
"in Password": "账号登录",
"in Captcha": "手机号登录",
"in QrCode": "扫码登录",
"Send": "发送验证码",
"placeholder": {
"Captcha": "输入4位短信验证码",
"Mobile": "请输入手机号",
"Password": "请输入密码"
}
}

1
lib/pages/login/web.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export default function render(this: any): JSX.Element;

64
lib/pages/login/web.js Normal file
View File

@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var validator_1 = require("oak-domain/lib/utils/validator");
var tdesign_icons_react_1 = require("tdesign-icons-react");
var tdesign_react_1 = require("tdesign-react");
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
var TabPanel = tdesign_react_1.Tabs.TabPanel;
var FormItem = tdesign_react_1.Form.FormItem;
function render() {
var _this = this;
var t = this.t;
var _a = this.props, onlyCaptcha = _a.onlyCaptcha, onlyPassword = _a.onlyPassword, width = _a.width;
var _b = this.state, mobile = _b.mobile, captcha = _b.captcha, password = _b.password, counter = _b.counter, _c = _b.tabValue, tabValue = _c === void 0 ? 1 : _c;
var validMobile = (0, validator_1.isMobile)(mobile);
var validCaptcha = (0, validator_1.isCaptcha)(captcha);
var validPassword = (0, validator_1.isPassword)(password);
var allowSubmit = validMobile && (validCaptcha || validPassword);
var LoginPassword = ((0, jsx_runtime_1.jsxs)(tdesign_react_1.Form, tslib_1.__assign({ colon: true, labelWidth: 0 }, { children: [(0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: mobile, type: "tel", "data-attr": "mobile", maxlength: 11, prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.MobileIcon, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (value, context) {
_this.setState({
mobile: value,
});
}, className: web_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "password" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: password, "data-attr": "password", prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.LockOnIcon, {}), type: "password", placeholder: t('placeholder.Password'), size: "large", onChange: function (value, context) {
_this.setState({
password: value,
});
}, className: web_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(FormItem, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ block: true, size: "large", theme: "primary", type: "submit", disabled: !allowSubmit, onClick: function () { return _this.loginByMobile(); } }, { children: t('Log in') })) })] })));
var LoginCaptcha = ((0, jsx_runtime_1.jsxs)(tdesign_react_1.Form, tslib_1.__assign({ colon: true, labelWidth: 0 }, { children: [(0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: mobile, "data-attr": "mobile", type: "tel", maxlength: 11, prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.MobileIcon, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (value, context) {
_this.setState({
mobile: value,
});
}, className: web_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "captcha" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: captcha, "data-attr": "captcha",
// type="number"
maxlength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: function (value, context) {
_this.setState({
captcha: value,
});
}, className: web_module_less_1.default['loginbox-input'], suffix: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ variant: "text", size: "small", theme: "primary", disabled: !validMobile || counter > 0, onClick: function () { return _this.sendCaptcha(); } }, { children: counter > 0 ? "".concat(counter, "\u79D2\u540E\u53EF\u91CD\u53D1") : t('Send') })) }) })), (0, jsx_runtime_1.jsx)(FormItem, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ block: true, size: "large", theme: "primary", type: "submit", disabled: !allowSubmit, onClick: function () { return _this.loginByMobile(); } }, { children: t('Log in') })) })] })));
if (onlyCaptcha) {
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-wrap'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-bd'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: (0, classnames_1.default)(web_module_less_1.default['loginbox-mobile'], web_module_less_1.default['loginbox-only']) }, { children: LoginCaptcha })) })) })) })));
}
else if (onlyPassword) {
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-wrap'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-bd'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: (0, classnames_1.default)(web_module_less_1.default['loginbox-password'], web_module_less_1.default['loginbox-only']) }, { children: LoginPassword })) })) })) })));
}
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-wrap'] }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-hd'] }, { children: (0, jsx_runtime_1.jsxs)(tdesign_react_1.Radio.Group, tslib_1.__assign({ variant: "default-filled", defaultValue: tabValue, onChange: function (value) {
_this.setState({
tabValue: value,
});
}, className: web_module_less_1.default['loginbox-hd__tab'] }, { children: [(0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 1, className: (0, classnames_1.default)(web_module_less_1.default['loginbox-hd__tabcon'], {
current: tabValue === 1,
}) }, { children: t('in Password') })), (0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 2, className: (0, classnames_1.default)(web_module_less_1.default['loginbox-hd__tabcon'], {
current: tabValue === 2,
}) }, { children: t('in Captcha') })), (0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 3, className: (0, classnames_1.default)(web_module_less_1.default['loginbox-hd__tabcon'], {
current: tabValue === 3,
}) }, { children: t('in QrCode') }))] })) })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-bd'] }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-password'], style: tabValue === 1 ? {} : { display: 'none' } }, { children: LoginPassword })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-mobile'], style: tabValue === 2 ? {} : { display: 'none' } }, { children: LoginCaptcha })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-qrcode'], style: tabValue === 3 ? {} : { display: 'none' } }, { children: [(0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-qrcode__sociallogin'] }, { children: ["\u8BF7\u4F7F\u7528\u5FAE\u4FE1\u626B\u4E00\u626B\u767B\u5F55", (0, jsx_runtime_1.jsxs)("span", tslib_1.__assign({ className: web_module_less_1.default['loginbox-qrcode__refresh'], onClick: function () {
_this.setMessage({
type: 'success',
content: '刷新二维码',
});
} }, { children: ["\u5237\u65B0", (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.Icon, { name: "refresh", className: web_module_less_1.default['loginbox-qrcode__refresh-icon'] })] }))] })), (0, jsx_runtime_1.jsx)("div", { className: web_module_less_1.default['loginbox-qrcode__iframe'] })] }))] })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-ft'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-ft__btn'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-protocal'] }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Checkbox, { label: (0, jsx_runtime_1.jsx)("div", { children: "\u9605\u8BFB\u5E76\u540C\u610F \u670D\u52A1\u6761\u6B3E \u548C \u9690\u79C1\u653F\u7B56" }) }) })) })) }))] })) })));
}
exports.default = render;

View File

@ -0,0 +1,110 @@
.loginbox-main {
height: 100vh;
display: flex;
flex: 1;
align-items: center;
flex-direction: column;
justify-content: center;
}
.loginbox-wrap {
width: 400px;
display: block;
background: #fff;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%);
}
.loginbox-hd {
padding: 32px;
&__tab {
width: 100%;
height: 38px;
background-color: rgba(0, 0, 0, .04) !important;
}
&__tabcon {
width: 100%;
display: flex;
justify-content: center;
}
}
.loginbox-bd {
height: 310px;
}
.loginbox-only {
padding-top: 32px !important;
}
.loginbox-mobile {
position: relative;
padding: 0 32px;
}
.loginbox-password {
position: relative;
padding: 0 32px;
}
.loginbox-qrcode {
padding: 0 32px;
font-size: 14px;
&__sociallogin {
margin-bottom: 15px;
text-align: center;
color: #999;
}
&__refresh {
color: var(--td-text-color-brand);
margin-left: 10px;
cursor: pointer;
&-icon {
color: var(--td-text-color-brand)
}
}
&__iframe {
position: relative;
width: 160px;
height: 160px;
margin: 0 auto;
border: #999 solid 1px;
}
}
.current {
color: var(--td-text-color-brand) !important;
cursor: default;
background-color: #fff;
border-radius: 4px;
}
.loginbox-input {
.t-input {
background-color: rgba(0, 0, 0, .04) !important;
}
}
.loginbox-ft {
height: 54px;
border-top: 1px solid #f2f3f5;
font-size: 14px;
&__btn {}
}
.loginbox-protocal {
padding: 20px 32px;
}

View File

@ -1,13 +1,8 @@
{
"Log in": "登录",
"Remember me": "记住账号",
"in Password": "账号登录",
"in Captcha": "手机号登录",
"in QrCode": "扫码登录",
"Log in": "进入",
"Send": "发送验证码",
"placeholder": {
"Captcha": "输入4位短信验证码",
"Mobile": "请输入手机号",
"Password": "请输入密码"
"Mobile": "请输入手机号"
}
}

View File

@ -5,60 +5,29 @@ var jsx_runtime_1 = require("react/jsx-runtime");
var validator_1 = require("oak-domain/lib/utils/validator");
var tdesign_icons_react_1 = require("tdesign-icons-react");
var tdesign_react_1 = require("tdesign-react");
var classnames_1 = tslib_1.__importDefault(require("classnames"));
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
var TabPanel = tdesign_react_1.Tabs.TabPanel;
var FormItem = tdesign_react_1.Form.FormItem;
function render() {
var _this = this;
var t = this.t;
var _a = this.props, onlyCaptcha = _a.onlyCaptcha, onlyPassword = _a.onlyPassword, width = _a.width;
var _b = this.state, mobile = _b.mobile, captcha = _b.captcha, password = _b.password, counter = _b.counter, _c = _b.tabValue, tabValue = _c === void 0 ? 1 : _c;
var width = this.props.width;
var _a = this.state, mobile = _a.mobile, captcha = _a.captcha, password = _a.password, counter = _a.counter, _b = _a.tabValue, tabValue = _b === void 0 ? 1 : _b;
var validMobile = (0, validator_1.isMobile)(mobile);
var validCaptcha = (0, validator_1.isCaptcha)(captcha);
var validPassword = (0, validator_1.isPassword)(password);
var allowSubmit = validMobile && (validCaptcha || validPassword);
var LoginPassword = ((0, jsx_runtime_1.jsxs)(tdesign_react_1.Form, tslib_1.__assign({ colon: true, labelWidth: 0 }, { children: [(0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: mobile, type: "tel", "data-attr": "mobile", maxlength: 11, prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.MobileIcon, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (value, context) {
_this.setState({
mobile: value,
});
}, className: "loginbox-input" }) })), (0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "password" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: password, "data-attr": "password", prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.LockOnIcon, {}), type: "password", placeholder: t('placeholder.Password'), size: "large", onChange: function (value, context) {
_this.setState({
password: value,
});
}, className: "loginbox-input" }) })), (0, jsx_runtime_1.jsx)(FormItem, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ block: true, size: "large", theme: "primary", type: "submit", disabled: !allowSubmit, onClick: function () { return _this.loginByMobile(); } }, { children: t('Log in') })) })] })));
var LoginCaptcha = ((0, jsx_runtime_1.jsxs)(tdesign_react_1.Form, tslib_1.__assign({ colon: true, labelWidth: 0 }, { children: [(0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: mobile, "data-attr": "mobile", type: "tel", maxlength: 11, prefixIcon: (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.MobileIcon, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (value, context) {
_this.setState({
mobile: value,
});
}, className: "loginbox-input" }) })), (0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "captcha" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: captcha, "data-attr": "captcha",
}, className: web_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(FormItem, tslib_1.__assign({ name: "captcha" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Input, { clearable: true, value: captcha, "data-attr": "captcha",
// type="number"
maxlength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: function (value, context) {
_this.setState({
captcha: value,
});
}, className: "loginbox-input", suffix: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ variant: "text", size: "small", theme: "primary", disabled: !validMobile || counter > 0, onClick: function () { return _this.sendCaptcha(); } }, { children: counter > 0 ? "".concat(counter, "\u79D2\u540E\u53EF\u91CD\u53D1") : t('Send') })) }) })), (0, jsx_runtime_1.jsx)(FormItem, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ block: true, size: "large", theme: "primary", type: "submit", disabled: !allowSubmit, onClick: function () { return _this.loginByMobile(); } }, { children: t('Log in') })) })] })));
if (onlyCaptcha) {
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-main" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-wrap" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-bd" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-mobile loginbox-only" }, { children: LoginCaptcha })) })) })) })));
}
else if (onlyPassword) {
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-main" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-wrap" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-bd" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-password loginbox-only" }, { children: LoginPassword })) })) })) })));
}
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "loginbox-wrap" }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-hd" }, { children: (0, jsx_runtime_1.jsxs)(tdesign_react_1.Radio.Group, tslib_1.__assign({ variant: "default-filled", defaultValue: tabValue, onChange: function (value) {
_this.setState({
tabValue: value,
});
}, className: "loginbox-hd__tab" }, { children: [(0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 1, className: (0, classnames_1.default)('loginbox-hd__tabcon', {
current: tabValue === 1,
}) }, { children: t('in Password') })), (0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 2, className: (0, classnames_1.default)('loginbox-hd__tabcon', {
current: tabValue === 2,
}) }, { children: t('in Captcha') })), (0, jsx_runtime_1.jsx)(tdesign_react_1.Radio.Button, tslib_1.__assign({ value: 3, className: (0, classnames_1.default)('loginbox-hd__tabcon', {
current: tabValue === 3,
}) }, { children: t('in QrCode') }))] })) })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "loginbox-bd" }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-password", style: tabValue === 1 ? {} : { display: 'none' } }, { children: LoginPassword })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-mobile", style: tabValue === 2 ? {} : { display: 'none' } }, { children: LoginCaptcha })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "loginbox-qrcode", style: tabValue === 3 ? {} : { display: 'none' } }, { children: [(0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "loginbox-qrcode__sociallogin" }, { children: ["\u8BF7\u4F7F\u7528\u5FAE\u4FE1\u626B\u4E00\u626B\u767B\u5F55", (0, jsx_runtime_1.jsxs)("span", tslib_1.__assign({ className: "loginbox-qrcode__refresh", onClick: function () {
_this.setMessage({
type: 'success',
content: '刷新二维码',
});
} }, { children: ["\u5237\u65B0", (0, jsx_runtime_1.jsx)(tdesign_icons_react_1.Icon, { name: "refresh", className: "loginbox-qrcode__refresh-icon" })] }))] })), (0, jsx_runtime_1.jsx)("div", { className: "loginbox-qrcode__iframe" })] }))] })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-ft" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-ft__btn" }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "loginbox-protocal" }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Checkbox, { label: (0, jsx_runtime_1.jsx)("div", { children: "\u9605\u8BFB\u5E76\u540C\u610F \u670D\u52A1\u6761\u6B3E \u548C \u9690\u79C1\u653F\u7B56" }) }) })) })) }))] })) })));
}, className: web_module_less_1.default['loginbox-input'], suffix: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ variant: "text", size: "small", theme: "primary", disabled: !validMobile || counter > 0, onClick: function () { return _this.sendCaptcha(); } }, { children: counter > 0 ? "".concat(counter, "\u79D2\u540E\u53EF\u91CD\u53D1") : t('Send') })) }) })), (0, jsx_runtime_1.jsx)(FormItem, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Button, tslib_1.__assign({ block: true, size: "large", theme: "primary", type: "submit", disabled: !allowSubmit, onClick: function () { return _this.loginByMobile(); } }, { children: t('Log in') })) })] })));
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-wrap'] }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-hd'] }, { children: "\u4E3A\u4E86\u66F4\u597D\u7684\u4F53\u9A8C\uFF0C\u8BF7\u5B8C\u5584\u8D26\u53F7\u4FE1\u606F" })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-bd'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-mobile'] }, { children: LoginCaptcha })) })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-ft'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-ft__btn'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-protocal'] }, { children: (0, jsx_runtime_1.jsx)(tdesign_react_1.Checkbox, { label: (0, jsx_runtime_1.jsx)("div", { children: "\u9605\u8BFB\u5E76\u540C\u610F \u670D\u52A1\u6761\u6B3E \u548C \u9690\u79C1\u653F\u7B56" }) }) })) })) }))] })) })));
}
exports.default = render;

View File

@ -4,7 +4,7 @@ import { OakUnloggedInException } from "oak-domain/lib/types/Exception";
export const routers: ExceptionRouters = [
[
OakUnloggedInException, {
router: '/mobile/login',
router: '/login',
}
]
];

View File

@ -0,0 +1,4 @@
{
"navigationBarTitleText": "登录",
"usingComponents": {}
}

View File

@ -0,0 +1,17 @@
/** index.wxss **/
@import "../../../config/styles/_base.less";
@import "../../../config/styles/_mixins.less";
.page-body {
height: 100vh;
display: flex;
flex: 1;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
background-color: @bg-color-block;
.safe-area-inset-bottom();
}

87
src/pages/login/index.ts Normal file
View File

@ -0,0 +1,87 @@
const SEND_KEY = 'captcha:sendAt';
export default OakPage({
path: 'login',
isList: false,
projection: {
id: 1,
mobile: 1,
userId: 1,
},
data: {
mobile: '',
password: '',
captcha: '',
counter: 0,
},
properties: {
onlyCaptcha: Boolean,
onlyPassword: Boolean,
eventLoggedIn: String,
},
async formData({ features }) {
const lastSendAt = features.localStorage.load(SEND_KEY);
const now = Date.now();
let counter = 0;
if (typeof lastSendAt === 'number') {
counter = Math.max(60 - Math.ceil((now - lastSendAt) / 1000), 0);
if (counter > 0) {
(this as any).counterHandler = setTimeout(() => this.reRender(), 1000);
}
else if ((this as any).counterHandler) {
clearTimeout((this as any).counterHandler);
(this as any).counterHandler = undefined;
}
}
return {
counter,
};
},
methods: {
onInput(e: any) {
const { dataset, value } = this.resolveInput(e);
const { attr } = dataset;
this.setState({
[attr]: value,
});
},
async sendCaptcha() {
const { mobile } = this.state;
try {
const result = await this.features.token.sendCaptcha(mobile);
// 显示返回消息
this.setMessage({
type: 'success',
content: result,
});
this.save(SEND_KEY, Date.now());
this.reRender();
}
catch (err) {
this.setMessage({
type: 'error',
content: (err as Error).message,
});
}
},
async loginByMobile() {
const { eventLoggedIn } = this.props;
const { mobile, password, captcha } = this.state;
try {
await this.features.token.loginByMobile(mobile, password, captcha);
if (eventLoggedIn) {
this.pub(eventLoggedIn);
}
else {
this.navigateBack();
}
}
catch (err) {
this.setMessage({
type: 'error',
content: (err as Error).message,
});
}
}
},
});

View File

@ -0,0 +1,13 @@
<!-- index.wxml -->
<view class="page-body">
<block wx:if="{{mobiles && mobiles.length > 0}}">
<view wx:for="{{mobiles}}" wx:key="index" class="card">
<text>{{item.mobile}}</text>
</view>
</block>
<block wx:else>
<view style="flex:1; display:flex; align-items:center;justify-content:center">尚未授权手机号</view>
</block>
<view style="display: flex; flex: 1"></view>
<t-button theme="primary" style="margin: 16rpx" block size="large" open-type="getPhoneNumber" bindgetphonenumber="onRefreshMobile" content="授权手机号" />
</view>

View File

@ -0,0 +1,13 @@
{
"Log in": "登录",
"Remember me": "记住账号",
"in Password": "账号登录",
"in Captcha": "手机号登录",
"in QrCode": "扫码登录",
"Send": "发送验证码",
"placeholder": {
"Captcha": "输入4位短信验证码",
"Mobile": "请输入手机号",
"Password": "请输入密码"
}
}

View File

@ -0,0 +1,110 @@
.loginbox-main {
height: 100vh;
display: flex;
flex: 1;
align-items: center;
flex-direction: column;
justify-content: center;
}
.loginbox-wrap {
width: 400px;
display: block;
background: #fff;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%);
}
.loginbox-hd {
padding: 32px;
&__tab {
width: 100%;
height: 38px;
background-color: rgba(0, 0, 0, .04) !important;
}
&__tabcon {
width: 100%;
display: flex;
justify-content: center;
}
}
.loginbox-bd {
height: 310px;
}
.loginbox-only {
padding-top: 32px !important;
}
.loginbox-mobile {
position: relative;
padding: 0 32px;
}
.loginbox-password {
position: relative;
padding: 0 32px;
}
.loginbox-qrcode {
padding: 0 32px;
font-size: 14px;
&__sociallogin {
margin-bottom: 15px;
text-align: center;
color: #999;
}
&__refresh {
color: var(--td-text-color-brand);
margin-left: 10px;
cursor: pointer;
&-icon {
color: var(--td-text-color-brand)
}
}
&__iframe {
position: relative;
width: 160px;
height: 160px;
margin: 0 auto;
border: #999 solid 1px;
}
}
.current {
color: var(--td-text-color-brand) !important;
cursor: default;
background-color: #fff;
border-radius: 4px;
}
.loginbox-input {
.t-input {
background-color: rgba(0, 0, 0, .04) !important;
}
}
.loginbox-ft {
height: 54px;
border-top: 1px solid #f2f3f5;
font-size: 14px;
&__btn {}
}
.loginbox-protocal {
padding: 20px 32px;
}

278
src/pages/login/web.tsx Normal file
View File

@ -0,0 +1,278 @@
import React from 'react';
import {
isMobile,
isPassword,
isCaptcha,
} from 'oak-domain/lib/utils/validator';
import { DesktopIcon, LockOnIcon, MobileIcon, Icon } from 'tdesign-icons-react';
import { Form, Input, Button, Checkbox, Tabs, Radio } from 'tdesign-react';
import classNames from 'classnames';
import Style from './web.module.less';
const { TabPanel } = Tabs;
const { FormItem } = Form;
export default function render(this: any) {
const { t } = this;
const { onlyCaptcha, onlyPassword, width } = this.props;
const { mobile, captcha, password, counter, tabValue = 1} = this.state;
const validMobile = isMobile(mobile);
const validCaptcha = isCaptcha(captcha);
const validPassword = isPassword(password);
const allowSubmit = validMobile && (validCaptcha || validPassword);
const LoginPassword = (
<Form colon={true} labelWidth={0}>
<FormItem name="mobile">
<Input
clearable
value={mobile}
type="tel"
data-attr="mobile"
maxlength={11}
prefixIcon={<MobileIcon />}
placeholder={t('placeholder.Mobile')}
size="large"
onChange={(value, context) => {
this.setState({
mobile: value,
});
}}
className={Style['loginbox-input']}
/>
</FormItem>
<FormItem name="password">
<Input
clearable
value={password}
data-attr="password"
prefixIcon={<LockOnIcon />}
type="password"
placeholder={t('placeholder.Password')}
size="large"
onChange={(value, context) => {
this.setState({
password: value,
});
}}
className={Style['loginbox-input']}
/>
</FormItem>
<FormItem>
<Button
block
size="large"
theme="primary"
type="submit"
disabled={!allowSubmit}
onClick={() => this.loginByMobile()}
>
{t('Log in')}
</Button>
</FormItem>
</Form>
);
const LoginCaptcha = (
<Form colon={true} labelWidth={0}>
<FormItem name="mobile">
<Input
clearable
value={mobile}
data-attr="mobile"
type="tel"
maxlength={11}
prefixIcon={<MobileIcon />}
placeholder={t('placeholder.Mobile')}
size="large"
onChange={(value, context) => {
this.setState({
mobile: value,
});
}}
className={Style['loginbox-input']}
/>
</FormItem>
<FormItem name="captcha">
<Input
clearable
value={captcha}
data-attr="captcha"
// type="number"
maxlength={4}
placeholder={t('placeholder.Captcha')}
size="large"
onChange={(value, context) => {
this.setState({
captcha: value,
});
}}
className={Style['loginbox-input']}
suffix={
<Button
variant="text"
size="small"
theme="primary"
disabled={!validMobile || counter > 0}
onClick={() => this.sendCaptcha()}
>
{counter > 0 ? `${counter}秒后可重发` : t('Send')}
</Button>
}
/>
</FormItem>
<FormItem>
<Button
block
size="large"
theme="primary"
type="submit"
disabled={!allowSubmit}
onClick={() => this.loginByMobile()}
>
{t('Log in')}
</Button>
</FormItem>
</Form>
);
if (onlyCaptcha) {
return (
<div className={Style['loginbox-main']}>
<div className={Style['loginbox-wrap']}>
<div className={Style['loginbox-bd']}>
<div
className={classNames(
Style['loginbox-mobile'],
Style['loginbox-only']
)}
>
{LoginCaptcha}
</div>
</div>
</div>
</div>
);
} else if (onlyPassword) {
return (
<div className={Style['loginbox-main']}>
<div className={Style['loginbox-wrap']}>
<div className={Style['loginbox-bd']}>
<div
className={classNames(
Style['loginbox-password'],
Style['loginbox-only']
)}
>
{LoginPassword}
</div>
</div>
</div>
</div>
);
}
return (
<div className={Style['loginbox-main']}>
<div className={Style['loginbox-wrap']}>
<div className={Style['loginbox-hd']}>
<Radio.Group
variant="default-filled"
defaultValue={tabValue}
onChange={(value) => {
this.setState({
tabValue: value,
});
}}
className={Style['loginbox-hd__tab']}
>
<Radio.Button
value={1}
className={classNames(
Style['loginbox-hd__tabcon'],
{
current: tabValue === 1,
}
)}
>
{t('in Password')}
</Radio.Button>
<Radio.Button
value={2}
className={classNames(
Style['loginbox-hd__tabcon'],
{
current: tabValue === 2,
}
)}
>
{t('in Captcha')}
</Radio.Button>
<Radio.Button
value={3}
className={classNames(
Style['loginbox-hd__tabcon'],
{
current: tabValue === 3,
}
)}
>
{t('in QrCode')}
</Radio.Button>
</Radio.Group>
</div>
<div className={Style['loginbox-bd']}>
<div
className={Style['loginbox-password']}
style={tabValue === 1 ? {} : { display: 'none' }}
>
{LoginPassword}
</div>
<div
className={Style['loginbox-mobile']}
style={tabValue === 2 ? {} : { display: 'none' }}
>
{LoginCaptcha}
</div>
<div
className={Style['loginbox-qrcode']}
style={tabValue === 3 ? {} : { display: 'none' }}
>
<div className={Style['loginbox-qrcode__sociallogin']}>
使
<span
className={Style['loginbox-qrcode__refresh']}
onClick={() => {
this.setMessage({
type: 'success',
content: '刷新二维码',
});
}}
>
<Icon
name="refresh"
className={
Style['loginbox-qrcode__refresh-icon']
}
/>
</span>
</div>
<div className={Style['loginbox-qrcode__iframe']}></div>
</div>
</div>
<div className={Style['loginbox-ft']}>
<div className={Style['loginbox-ft__btn']}>
<div className={Style['loginbox-protocal']}>
<Checkbox
label={
<div> </div>
}
/>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,13 +1,8 @@
{
"Log in": "登录",
"Remember me": "记住账号",
"in Password": "账号登录",
"in Captcha": "手机号登录",
"in QrCode": "扫码登录",
"Log in": "进入",
"Send": "发送验证码",
"placeholder": {
"Captcha": "输入4位短信验证码",
"Mobile": "请输入手机号",
"Password": "请输入密码"
"Mobile": "请输入手机号"
}
}

View File

@ -14,65 +14,13 @@ const { FormItem } = Form;
export default function render(this: any) {
const { t } = this;
const { onlyCaptcha, onlyPassword, width } = this.props;
const { mobile, captcha, password, counter, tabValue = 1} = this.state;
const { width } = this.props;
const { mobile, captcha, password, counter, tabValue = 1 } = this.state;
const validMobile = isMobile(mobile);
const validCaptcha = isCaptcha(captcha);
const validPassword = isPassword(password);
const allowSubmit = validMobile && (validCaptcha || validPassword);
const LoginPassword = (
<Form colon={true} labelWidth={0}>
<FormItem name="mobile">
<Input
clearable
value={mobile}
type="tel"
data-attr="mobile"
maxlength={11}
prefixIcon={<MobileIcon />}
placeholder={t('placeholder.Mobile')}
size="large"
onChange={(value, context) => {
this.setState({
mobile: value,
});
}}
className="loginbox-input"
/>
</FormItem>
<FormItem name="password">
<Input
clearable
value={password}
data-attr="password"
prefixIcon={<LockOnIcon />}
type="password"
placeholder={t('placeholder.Password')}
size="large"
onChange={(value, context) => {
this.setState({
password: value,
});
}}
className="loginbox-input"
/>
</FormItem>
<FormItem>
<Button
block
size="large"
theme="primary"
type="submit"
disabled={!allowSubmit}
onClick={() => this.loginByMobile()}
>
{t('Log in')}
</Button>
</FormItem>
</Form>
);
const LoginCaptcha = (
<Form colon={true} labelWidth={0}>
<FormItem name="mobile">
@ -90,7 +38,7 @@ export default function render(this: any) {
mobile: value,
});
}}
className="loginbox-input"
className={Style['loginbox-input']}
/>
</FormItem>
<FormItem name="captcha">
@ -107,7 +55,7 @@ export default function render(this: any) {
captcha: value,
});
}}
className="loginbox-input"
className={Style['loginbox-input']}
suffix={
<Button
variant="text"
@ -137,112 +85,22 @@ export default function render(this: any) {
</Form>
);
if (onlyCaptcha) {
return (
<div className="loginbox-main">
<div className="loginbox-wrap">
<div className="loginbox-bd">
<div className="loginbox-mobile loginbox-only">
{LoginCaptcha}
</div>
</div>
</div>
</div>
);
} else if (onlyPassword) {
return (
<div className="loginbox-main">
<div className="loginbox-wrap">
<div className="loginbox-bd">
<div className="loginbox-password loginbox-only">
{LoginPassword}
</div>
</div>
</div>
</div>
);
}
return (
<div className={Style['loginbox-main']}>
<div className="loginbox-wrap">
<div className="loginbox-hd">
<Radio.Group
variant="default-filled"
defaultValue={tabValue}
onChange={(value) => {
this.setState({
tabValue: value,
});
}}
className="loginbox-hd__tab"
>
<Radio.Button
value={1}
className={classNames('loginbox-hd__tabcon', {
current: tabValue === 1,
})}
>
{t('in Password')}
</Radio.Button>
<Radio.Button
value={2}
className={classNames('loginbox-hd__tabcon', {
current: tabValue === 2,
})}
>
{t('in Captcha')}
</Radio.Button>
<Radio.Button
value={3}
className={classNames('loginbox-hd__tabcon', {
current: tabValue === 3,
})}
>
{t('in QrCode')}
</Radio.Button>
</Radio.Group>
<div className={Style['loginbox-wrap']}>
<div className={Style['loginbox-hd']}>
</div>
<div className="loginbox-bd">
<div className={Style['loginbox-bd']}>
<div
className="loginbox-password"
style={tabValue === 1 ? {} : { display: 'none' }}
>
{LoginPassword}
</div>
<div
className="loginbox-mobile"
style={tabValue === 2 ? {} : { display: 'none' }}
className={Style['loginbox-mobile']}
>
{LoginCaptcha}
</div>
<div
className="loginbox-qrcode"
style={tabValue === 3 ? {} : { display: 'none' }}
>
<div className="loginbox-qrcode__sociallogin">
使
<span
className="loginbox-qrcode__refresh"
onClick={() => {
this.setMessage({
type: 'success',
content: '刷新二维码',
});
}}
>
<Icon
name="refresh"
className="loginbox-qrcode__refresh-icon"
/>
</span>
</div>
<div className="loginbox-qrcode__iframe"></div>
</div>
</div>
<div className="loginbox-ft">
<div className="loginbox-ft__btn">
<div className="loginbox-protocal">
<div className={Style['loginbox-ft']}>
<div className={Style['loginbox-ft__btn']}>
<div className={Style['loginbox-protocal']}>
<Checkbox
label={
<div> </div>