feat: 新增用户账号注册组件;登录组件适配账号登录
This commit is contained in:
parent
b93ae46894
commit
d7d302f329
|
|
@ -735,5 +735,16 @@ export type AspectDict<ED extends EntityDict> = {
|
|||
setUserAvatarFromWechat: (params: {
|
||||
avatar: string;
|
||||
}, context: BackendRuntimeContext<ED>) => Promise<void>;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param loginName 账号
|
||||
* @param password 密码
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
registerUserByLoginName: (params: {
|
||||
loginName: string;
|
||||
password: string;
|
||||
}, context: BackendRuntimeContext<ED>) => Promise<void>;
|
||||
};
|
||||
export default AspectDict;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { getApplication, signatureJsSDK, uploadWechatMedia, batchGetArticle, get
|
|||
import { updateConfig, updateApplicationConfig, updateStyle } from './config';
|
||||
import { syncMessageTemplate, getMessageType } from './template';
|
||||
import { syncSmsTemplate } from './sms';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword, registerUserByLoginName } from './user';
|
||||
import { createWechatLogin } from './wechatLogin';
|
||||
import { unbindingWechat } from './wechatUser';
|
||||
import { getMpUnlimitWxaCode } from './wechatQrCode';
|
||||
|
|
@ -86,6 +86,7 @@ declare const aspectDict: {
|
|||
createOAuthState: typeof createOAuthState;
|
||||
authorize: typeof authorize;
|
||||
setUserAvatarFromWechat: typeof setUserAvatarFromWechat;
|
||||
registerUserByLoginName: typeof registerUserByLoginName;
|
||||
};
|
||||
export default aspectDict;
|
||||
export { AspectDict } from './AspectDict';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { getApplication, signatureJsSDK, uploadWechatMedia, batchGetArticle, get
|
|||
import { updateConfig, updateApplicationConfig, updateStyle } from './config';
|
||||
import { syncMessageTemplate, getMessageType } from './template';
|
||||
import { syncSmsTemplate } from './sms';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword, registerUserByLoginName } from './user';
|
||||
import { createWechatLogin } from './wechatLogin';
|
||||
import { unbindingWechat } from './wechatUser';
|
||||
import { getMpUnlimitWxaCode } from './wechatQrCode';
|
||||
|
|
@ -87,5 +87,6 @@ const aspectDict = {
|
|||
createOAuthState,
|
||||
authorize,
|
||||
setUserAvatarFromWechat,
|
||||
registerUserByLoginName,
|
||||
};
|
||||
export default aspectDict;
|
||||
|
|
|
|||
|
|
@ -23,3 +23,12 @@ export declare function updateUserPassword<ED extends EntityDict>(params: {
|
|||
result: string;
|
||||
times?: undefined;
|
||||
}>;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param params
|
||||
* @param context
|
||||
*/
|
||||
export declare function registerUserByLoginName<ED extends EntityDict>(params: {
|
||||
loginName: string;
|
||||
password: string;
|
||||
}, context: BRC<ED>): Promise<void>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { OakOperationUnpermittedException } from "oak-domain/lib/types";
|
||||
import { OakOperationUnpermittedException, OakPreConditionUnsetException } from "oak-domain/lib/types";
|
||||
import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
|
||||
import { encryptPasswordSha1 } from '../utils/password';
|
||||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
|
|
@ -177,20 +177,17 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
const [passport] = await context.select('passport', {
|
||||
const [system] = await context.select('system', {
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
systemId: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId,
|
||||
type: 'password',
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
assert(passport);
|
||||
const config = passport.config;
|
||||
assert(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
const [user] = await context.select('user', {
|
||||
data: {
|
||||
|
|
@ -257,13 +254,13 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
const allowUpdate = mode === 'sha1' ? user.passwordSha1 === prevPassword : user.password === prevPassword; //sha1密文模式判断密文是否相等
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
prevPasswordSha1: encryptPasswordSha1(prevPassword),
|
||||
|
|
@ -271,19 +268,19 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: prevPassword,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -292,7 +289,7 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
await context.operate('user', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -306,7 +303,7 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -324,7 +321,7 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'fail',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -353,13 +350,13 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
dontCollect: true,
|
||||
});
|
||||
if (aliveCaptcha) {
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
|
|
@ -367,19 +364,19 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -387,7 +384,7 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
await context.operate('user', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -401,7 +398,7 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -428,3 +425,83 @@ export async function updateUserPassword(params, context, innerLogic) {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param params
|
||||
* @param context
|
||||
*/
|
||||
export async function registerUserByLoginName(params, context) {
|
||||
const { loginName, password } = params;
|
||||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
// 检查loginName是否重复
|
||||
const [existLoginName] = await context.select('loginName', {
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
filter: {
|
||||
name: loginName,
|
||||
ableState: 'enabled',
|
||||
},
|
||||
}, { dontCollect: true, forUpdate: true });
|
||||
if (existLoginName) {
|
||||
closeRootMode();
|
||||
throw new OakPreConditionUnsetException('账号已存在,请重新设置');
|
||||
}
|
||||
// 创建user并附上密码,级联创建loginName
|
||||
const [system] = await context.select('system', {
|
||||
data: {
|
||||
id: 1,
|
||||
config: 1,
|
||||
},
|
||||
filter: {
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
assert(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
let passwordData = {};
|
||||
if (mode === 'all') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
passwordSha1: encryptPasswordSha1(password),
|
||||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
passwordData = {
|
||||
passwordSha1: password,
|
||||
};
|
||||
}
|
||||
const userData = {
|
||||
id: await generateNewIdAsync(),
|
||||
loginName$user: [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
name: loginName,
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
Object.assign(userData, passwordData);
|
||||
await context.operate('user', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: userData,
|
||||
}, {});
|
||||
}
|
||||
catch (err) {
|
||||
closeRootMode();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
setLoginMode: (value) => undefined,
|
||||
digit: 4, //验证码位数
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,5 +6,8 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
redirectUri: string;
|
||||
url: string;
|
||||
callback: (() => void) | undefined;
|
||||
goRegister: (() => void) | undefined;
|
||||
isRegisterBack: boolean;
|
||||
goOauthLogin: ((oauthProviderId: string) => void) | undefined;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -12,79 +12,104 @@ export default OakComponent({
|
|||
passportTypes: [],
|
||||
inputOptions: [],
|
||||
scanOptions: [],
|
||||
allowSms: false,
|
||||
allowEmail: false,
|
||||
oauthOptions: [],
|
||||
allowMobile: false, //密码登录允许使用手机号
|
||||
allowEmail: false, //密码登录允许使用邮箱
|
||||
allowLoginName: false, //密码登录允许使用账号
|
||||
allowPassword: false,
|
||||
allowSms: false, //小程序使用
|
||||
allowWechatMp: false,
|
||||
setLoginModeMp(value) { this.setLoginMode(value); },
|
||||
smsDigit: 4,
|
||||
emailDigit: 4,
|
||||
smsDigit: 4, //短信验证码位数
|
||||
emailDigit: 4, //邮箱验证码位数
|
||||
pwdMode: 'all', //密码明文密文存储模式
|
||||
allowRegister: false, //开启账号登录且允许注册
|
||||
},
|
||||
properties: {
|
||||
onlyCaptcha: false,
|
||||
onlyPassword: false,
|
||||
disabled: '',
|
||||
redirectUri: '',
|
||||
url: '',
|
||||
redirectUri: '', // 微信登录后的redirectUri,要指向wechatUser/login去处理
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
goRegister: undefined, //跳转注册
|
||||
isRegisterBack: false, //从注册页跳回登录时将优先选中账号登录方式
|
||||
goOauthLogin: undefined //跳转指定第三方授权
|
||||
},
|
||||
formData({ features, props }) {
|
||||
return {};
|
||||
},
|
||||
listeners: {
|
||||
// 'onlyPassword,onlyCaptcha'(prev, next) {
|
||||
// let loginMode = this.state.loginMode, inputOptions = this.state.inputOptions, scanOptions = this.state.scanOptions;
|
||||
// if (next.onlyPassword) {
|
||||
// loginMode = 'password';
|
||||
// inputOptions = [{
|
||||
// label: this.t('passport:v.type.password'),
|
||||
// value: 'password',
|
||||
// }];
|
||||
// } else if (next.onlyCaptcha) {
|
||||
// loginMode = 'sms';
|
||||
// inputOptions = [{
|
||||
// label: this.t('passport:v.type.sms'),
|
||||
// value: 'sms',
|
||||
// }];
|
||||
// } else {
|
||||
// const { passportTypes } = this.state;
|
||||
// if (passportTypes && passportTypes.length > 0) {
|
||||
// passportTypes.forEach((ele: EntityDict['passport']['Schema']['type']) => {
|
||||
// if (ele === 'sms' || ele === 'email' || ele === 'password') {
|
||||
// inputOptions.push({
|
||||
// label: this.t(`passport:v.type.${ele}`),
|
||||
// value: ele
|
||||
// })
|
||||
// } else if (ele === 'wechatMpForWeb' || ele === 'wechatPublicForWeb') {
|
||||
// scanOptions.push({
|
||||
// label: this.t(`passport:v.type.${ele}`),
|
||||
// value: ele
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// this.setState({
|
||||
// loginMode,
|
||||
// inputOptions,
|
||||
// scanOptions,
|
||||
// })
|
||||
// }
|
||||
// 'onlyPassword,onlyCaptcha'(prev, next) {
|
||||
// let loginMode = this.state.loginMode, inputOptions = this.state.inputOptions, scanOptions = this.state.scanOptions;
|
||||
// if (next.onlyPassword) {
|
||||
// loginMode = 'password';
|
||||
// inputOptions = [{
|
||||
// label: this.t('passport:v.type.password'),
|
||||
// value: 'password',
|
||||
// }];
|
||||
// } else if (next.onlyCaptcha) {
|
||||
// loginMode = 'sms';
|
||||
// inputOptions = [{
|
||||
// label: this.t('passport:v.type.sms'),
|
||||
// value: 'sms',
|
||||
// }];
|
||||
// } else {
|
||||
// const { passportTypes } = this.state;
|
||||
// if (passportTypes && passportTypes.length > 0) {
|
||||
// passportTypes.forEach((ele: EntityDict['passport']['Schema']['type']) => {
|
||||
// if (ele === 'sms' || ele === 'email' || ele === 'password') {
|
||||
// inputOptions.push({
|
||||
// label: this.t(`passport:v.type.${ele}`),
|
||||
// value: ele
|
||||
// })
|
||||
// } else if (ele === 'wechatMpForWeb' || ele === 'wechatPublicForWeb') {
|
||||
// scanOptions.push({
|
||||
// label: this.t(`passport:v.type.${ele}`),
|
||||
// value: ele
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// this.setState({
|
||||
// loginMode,
|
||||
// inputOptions,
|
||||
// scanOptions,
|
||||
// })
|
||||
// }
|
||||
isRegisterBack(prev, next) {
|
||||
if (prev.isRegisterBack !== next.isRegisterBack && next.isRegisterBack) {
|
||||
const { passportTypes } = this.state;
|
||||
const { onlyCaptcha } = this.props;
|
||||
if (passportTypes.includes('loginName') && !onlyCaptcha) {
|
||||
this.setState({
|
||||
loginMode: 'password'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
const { isRegisterBack } = this.props;
|
||||
const application = this.features.application.getApplication();
|
||||
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||
const defaultPassport = applicationPassports.find((ele) => ele.isDefault);
|
||||
const passportTypes = applicationPassports.map((ele) => ele.passport.type);
|
||||
const smsDigit = applicationPassports.find((ele) => ele.passport.type === 'sms')?.passport.config.digit || 4;
|
||||
const emailDigit = applicationPassports.find((ele) => ele.passport.type === 'email')?.passport.config.digit || 4;
|
||||
const pwdMode = applicationPassports.find((ele) => ele.passport.type === 'password')?.passport.config.mode || 'all';
|
||||
const pwdConfig = application?.system?.config?.Password;
|
||||
const pwdMode = pwdConfig?.mode || 'all';
|
||||
const { onlyCaptcha, onlyPassword } = this.props;
|
||||
const smsAP = applicationPassports.find((ele) => ele.passport.type === 'sms');
|
||||
const loginNameAP = applicationPassports.find((ele) => ele.passport.type === 'loginName');
|
||||
const emailAP = applicationPassports.find((ele) => ele.passport.type === 'email');
|
||||
const showPassword = (smsAP && smsAP?.allowPwd) || (loginNameAP && loginNameAP?.allowPwd) || (emailAP && emailAP?.allowPwd); //(手机号、账号、邮箱登录中)存在至少一种开启密码登录的登录方式且非仅手机验证码登录
|
||||
let loginMode = (await this.load(LOGIN_MODE)) || defaultPassport?.passport?.type || 'sms';
|
||||
let inputOptions = [], scanOptions = [];
|
||||
if (onlyPassword) {
|
||||
let oauthOptions = [];
|
||||
if (onlyPassword && showPassword) {
|
||||
loginMode = 'password';
|
||||
inputOptions = [{
|
||||
label: this.t('passport:v.type.password') + this.t('Login'),
|
||||
|
|
@ -99,8 +124,14 @@ export default OakComponent({
|
|||
}];
|
||||
}
|
||||
else {
|
||||
if (showPassword) {
|
||||
inputOptions.push({
|
||||
label: this.t(`passport:v.type.password`) + this.t('Login'),
|
||||
value: 'password'
|
||||
});
|
||||
}
|
||||
passportTypes.forEach((ele) => {
|
||||
if (ele === 'sms' || ele === 'email' || ele === 'password') {
|
||||
if (ele === 'sms' || ele === 'email') {
|
||||
inputOptions.push({
|
||||
label: this.t(`passport:v.type.${ele}`) + this.t('Login'),
|
||||
value: ele
|
||||
|
|
@ -113,8 +144,36 @@ export default OakComponent({
|
|||
});
|
||||
}
|
||||
});
|
||||
const oauthAp = applicationPassports.find((ele) => ele.passport.type === 'oauth');
|
||||
const { oauthIds } = oauthAp?.passport?.config || {};
|
||||
if (oauthIds && oauthIds.length > 0) {
|
||||
const { data: oauthProviders } = await this.features.cache.refresh('oauthProvider', {
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
logo: 1,
|
||||
},
|
||||
filter: {
|
||||
id: {
|
||||
$in: oauthIds,
|
||||
}
|
||||
}
|
||||
});
|
||||
if (oauthProviders && oauthProviders?.length > 0) {
|
||||
oauthOptions = oauthProviders?.map((ele) => {
|
||||
return {
|
||||
name: ele.name,
|
||||
value: ele.id,
|
||||
logo: ele.logo ?? undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!passportTypes.includes(loginMode)) {
|
||||
if (isRegisterBack && !onlyCaptcha) {
|
||||
loginMode = 'password';
|
||||
}
|
||||
if ((loginMode !== 'password' && !passportTypes.includes(loginMode)) || (loginMode === 'password' && !showPassword)) {
|
||||
loginMode = defaultPassport.passport.type;
|
||||
}
|
||||
const appType = application?.type;
|
||||
|
|
@ -133,10 +192,13 @@ export default OakComponent({
|
|||
appId = config2?.wechat?.appId;
|
||||
domain = config2?.wechat?.domain;
|
||||
}
|
||||
const allowSms = passportTypes.includes('sms') && !onlyPassword;
|
||||
const allowEmail = passportTypes.includes('email') && !onlyCaptcha && !onlyPassword;
|
||||
const allowPassword = passportTypes.includes('password') && !onlyCaptcha;
|
||||
const allowMobile = smsAP && smsAP?.allowPwd;
|
||||
const allowEmail = emailAP && emailAP?.allowPwd;
|
||||
const allowLoginName = loginNameAP && loginNameAP?.allowPwd;
|
||||
const allowWechatMp = passportTypes.includes('wechatMp') && !onlyCaptcha && !onlyPassword;
|
||||
const allowPassword = !onlyCaptcha && showPassword;
|
||||
const allowSms = passportTypes.includes('sms') && !onlyPassword;
|
||||
const allowRegister = loginNameAP && loginNameAP?.passport?.config?.register;
|
||||
this.setState({
|
||||
loginMode,
|
||||
appId,
|
||||
|
|
@ -145,10 +207,14 @@ export default OakComponent({
|
|||
passportTypes,
|
||||
inputOptions,
|
||||
scanOptions,
|
||||
allowSms,
|
||||
oauthOptions,
|
||||
allowMobile,
|
||||
allowEmail,
|
||||
allowLoginName,
|
||||
allowPassword,
|
||||
allowSms,
|
||||
allowWechatMp,
|
||||
allowRegister,
|
||||
smsDigit,
|
||||
emailDigit,
|
||||
pwdMode,
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@
|
|||
"resendAfter": "秒后可重发",
|
||||
"otherMethods": "其他登录方式",
|
||||
"scanLogin": "扫码登录",
|
||||
"tip": "未注册用户首次登录将自动注册"
|
||||
"tip": "未注册用户首次登录将自动注册",
|
||||
"goRegister": "去注册"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
|
|||
redirectUri: string;
|
||||
url: string;
|
||||
callback: (() => void) | undefined;
|
||||
allowSms: boolean;
|
||||
allowMobile: boolean;
|
||||
allowEmail: boolean;
|
||||
allowLoginName: boolean;
|
||||
allowSms: boolean;
|
||||
allowWechatMp: boolean;
|
||||
setLoginMode: (value: string) => void;
|
||||
pwdMode: string;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,21 @@ import { encryptPasswordSha1 } from "../../../../utils/password";
|
|||
export default OakComponent({
|
||||
isList: false,
|
||||
formData({ features, props }) {
|
||||
return {};
|
||||
const { allowMobile, allowEmail, allowLoginName } = this.props;
|
||||
let tips = [];
|
||||
if (allowLoginName) {
|
||||
tips.push(this.t('placeholder.LoginName'));
|
||||
}
|
||||
if (allowMobile) {
|
||||
tips.push(this.t('placeholder.Mobile'));
|
||||
}
|
||||
if (allowEmail) {
|
||||
tips.push(this.t('placeholder.Email'));
|
||||
}
|
||||
const accountPlaceholder = tips.length > 0 ? this.t('placeholder.Account') + tips.join('/') : this.t('placeholder.Account');
|
||||
return {
|
||||
accountPlaceholder,
|
||||
};
|
||||
},
|
||||
data: {
|
||||
counter: 0,
|
||||
|
|
@ -19,12 +33,14 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
redirectUri: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
allowSms: false,
|
||||
redirectUri: '', // 微信登录后的redirectUri,要指向wechatUser/login去处理
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
allowMobile: false,
|
||||
allowEmail: false,
|
||||
allowWechatMp: false,
|
||||
allowLoginName: false,
|
||||
allowSms: false, //小程序切换手机号验证码登录
|
||||
allowWechatMp: false, //小程序切换授权登录
|
||||
setLoginMode: (value) => undefined,
|
||||
pwdMode: 'all', //密码明文密文存储模式
|
||||
},
|
||||
|
|
@ -86,10 +102,10 @@ export default OakComponent({
|
|||
}
|
||||
},
|
||||
inputChange(type, value) {
|
||||
const { allowSms, allowEmail } = this.props;
|
||||
const { allowMobile, allowEmail } = this.props;
|
||||
switch (type) {
|
||||
case 'account':
|
||||
// const validMobile = allowSms && !!isMobile(value);
|
||||
// const validMobile = allowMobile && !!isMobile(value);
|
||||
// const vaildEmail = allowEmail && !!isEmail(value);
|
||||
const vaildAccount = !!(value && value.trim() && value.trim() !== '');
|
||||
this.setState({
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<oak-icon name="mine" size="28" color="#808080" />
|
||||
<l-input
|
||||
hide-label="{{true}}"
|
||||
placeholder="{{t('placeholder.Account')}}"
|
||||
placeholder="{{accountPlaceholder}}"
|
||||
clear="{{true}}"
|
||||
showRow="{{false}}"
|
||||
l-class="my-input"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
{
|
||||
"Login": "登录",
|
||||
"placeholder": {
|
||||
"Account": "请输入账号",
|
||||
"Mobile": "/手机号",
|
||||
"Email": "/邮箱",
|
||||
"Account": "请输入",
|
||||
"LoginName": "账号",
|
||||
"Mobile": "手机号",
|
||||
"Email": "邮箱",
|
||||
"Password": "请输入密码"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'token', fal
|
|||
validMobile: boolean;
|
||||
validPassword: boolean;
|
||||
allowSubmit: boolean;
|
||||
allowSms: boolean;
|
||||
allowEmail: boolean;
|
||||
accountPlaceholder: string;
|
||||
}, {
|
||||
loginByAccount: () => Promise<void>;
|
||||
inputChange: (type: 'account' | 'password', value: string) => void;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { LockOutlined, UserOutlined, } from '@ant-design/icons';
|
|||
import Style from './web.module.less';
|
||||
export default function Render(props) {
|
||||
const { data, methods } = props;
|
||||
const { loading, disabled, account, password, validMobile, validPassword, allowSubmit, allowSms, allowEmail, } = data;
|
||||
const { loading, disabled, account, password, validMobile, validPassword, allowSubmit, accountPlaceholder } = data;
|
||||
const { loginByAccount, t, inputChange } = methods;
|
||||
return (<Form colon={true}>
|
||||
<Form.Item name="mobile">
|
||||
<Input allowClear value={account} size="large"
|
||||
// maxLength={11}
|
||||
prefix={<UserOutlined />} placeholder={t('placeholder.Account') + (allowSms ? t('placeholder.Mobile') : '') + (allowEmail ? t('placeholder.Email') : '')} onChange={(e) => {
|
||||
prefix={<UserOutlined />} placeholder={accountPlaceholder} onChange={(e) => {
|
||||
inputChange('account', e.target.value);
|
||||
}} className={Style['loginbox-input']}/>
|
||||
</Form.Item>
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ export default OakComponent({
|
|||
},
|
||||
properties: {
|
||||
disabled: '',
|
||||
url: '',
|
||||
callback: undefined,
|
||||
allowPassword: false,
|
||||
allowWechatMp: false,
|
||||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined, // 登录成功回调,排除微信登录方式
|
||||
allowPassword: false, //小程序切换密码登录
|
||||
allowWechatMp: false, //小程序切换授权登录
|
||||
setLoginMode: (value) => undefined,
|
||||
digit: 4 //验证码位数,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,9 +21,18 @@ export default function Render(props: WebComponentProps<EntityDict, 'token', fal
|
|||
scanOptions: Option[];
|
||||
smsDigit: number;
|
||||
emailDigit: number;
|
||||
allowSms: boolean;
|
||||
allowMobile: boolean;
|
||||
allowEmail: boolean;
|
||||
allowLoginName: boolean;
|
||||
pwdMode: 'all' | 'plain' | 'sha1';
|
||||
allowRegister: boolean;
|
||||
oauthOptions: {
|
||||
name: string;
|
||||
value: string;
|
||||
logo?: string;
|
||||
}[];
|
||||
goRegister: () => void;
|
||||
goOauthLogin: (oauthProviderId: string) => void;
|
||||
}, {
|
||||
setLoginMode: (value: number) => void;
|
||||
}>): React.JSX.Element;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// @ts-nocheck
|
||||
// Segmented这个对象在antd里的声明是错误的
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Segmented, Divider, Space } from 'antd';
|
||||
import { MobileOutlined, QrcodeOutlined, DesktopOutlined, MailOutlined, ExclamationCircleOutlined, } from '@ant-design/icons';
|
||||
import { Button, Segmented, Divider, Space, Tooltip, Image } from 'antd';
|
||||
import { MobileOutlined, QrcodeOutlined, DesktopOutlined, MailOutlined, ExclamationCircleOutlined, LinkOutlined, } from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
import Style from './web.module.less';
|
||||
import WeChatLoginQrCode from '../../common/weChatLoginQrCode';
|
||||
|
|
@ -13,8 +13,8 @@ import PasswordLogin from './password';
|
|||
import EmailLogin from './email';
|
||||
export default function Render(props) {
|
||||
const { data, methods } = props;
|
||||
const { width, loading, loginMode, appId, domain, isSupportWechatGrant, disabled, redirectUri, url, passportTypes, callback, inputOptions, scanOptions, smsDigit, emailDigit, allowSms, allowEmail, pwdMode, } = data;
|
||||
const { t, setLoginMode } = methods;
|
||||
const { width, loading, loginMode, appId, domain, isSupportWechatGrant, disabled, redirectUri, url, passportTypes, callback, inputOptions, scanOptions, smsDigit, emailDigit, allowMobile, allowEmail, allowLoginName, pwdMode, allowRegister, oauthOptions, goRegister, goOauthLogin, } = data;
|
||||
const { t, setLoginMode, } = methods;
|
||||
let redirectUri2 = redirectUri;
|
||||
if (!(redirectUri.startsWith('https') || redirectUri.startsWith('http'))) {
|
||||
const hostname = domain || window.location.hostname;
|
||||
|
|
@ -36,10 +36,11 @@ export default function Render(props) {
|
|||
setShowInput(false);
|
||||
}
|
||||
}, [loginMode]);
|
||||
const InputMethods = inputOptions && inputOptions.length > 0 ? (<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24 }}>
|
||||
{inputOptions.map((ele) => {
|
||||
const InputMethods = inputOptions && inputOptions.length > 0 ? (
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24 }}>
|
||||
{inputOptions.map((ele) => {
|
||||
let icon = <></>;
|
||||
if (ele.value === 'sms') {
|
||||
icon = <MobileOutlined />;
|
||||
|
|
@ -53,21 +54,36 @@ export default function Render(props) {
|
|||
return (<Space key={ele.value} size={4} style={{ cursor: 'pointer' }} onClick={() => {
|
||||
setLoginMode(ele.value);
|
||||
}}>
|
||||
{icon}
|
||||
<div>{ele.label}</div>
|
||||
</Space>);
|
||||
{icon}
|
||||
<div>{ele.label}</div>
|
||||
</Space>);
|
||||
})}
|
||||
</div>
|
||||
</div>) : <></>;
|
||||
const ScanMethods = scanOptions && scanOptions.length > 0 ? (<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space style={{ cursor: 'pointer' }} onClick={() => {
|
||||
</div>
|
||||
// </div>
|
||||
) : <></>;
|
||||
const ScanMethods = scanOptions && scanOptions.length > 0 ? (
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space style={{ cursor: 'pointer' }} onClick={() => {
|
||||
setLoginMode(scanOptions[0].value);
|
||||
}}>
|
||||
<QrcodeOutlined />
|
||||
<div>{t('scanLogin')}</div>
|
||||
</Space>
|
||||
</div>) : <></>;
|
||||
<QrcodeOutlined />
|
||||
<div>{t('scanLogin')}</div>
|
||||
</Space>
|
||||
// </div>
|
||||
) : <></>;
|
||||
const OauthMethods = oauthOptions && oauthOptions.length > 0 ? (
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space style={{ cursor: 'pointer' }}>
|
||||
{oauthOptions?.map((ele) => (<Tooltip title={ele.name}>
|
||||
<Image preview={false} width={20} height={20} src={ele.logo} onClick={() => {
|
||||
goOauthLogin(ele.value);
|
||||
}} placeholder={<LinkOutlined />}/>
|
||||
</Tooltip>))}
|
||||
</Space>
|
||||
// </div>
|
||||
) : <></>;
|
||||
const Tip = <div className={Style['loginbox-tip']}>
|
||||
<Space>
|
||||
<ExclamationCircleOutlined />
|
||||
|
|
@ -97,8 +113,13 @@ export default function Render(props) {
|
|||
<div className={Style['loginbox-password']} style={{
|
||||
display: loginMode === 'password' ? 'block' : 'none',
|
||||
}}>
|
||||
<PasswordLogin disabled={disabled} url={url} callback={callback} allowSms={allowSms} allowEmail={allowEmail} pwdMode={pwdMode}/>
|
||||
{Tip}
|
||||
<PasswordLogin disabled={disabled} url={url} callback={callback} allowMobile={allowMobile} allowEmail={allowEmail} allowLoginName={allowLoginName} pwdMode={pwdMode}/>
|
||||
{allowRegister && (<div className={Style['loginbox-register']}>
|
||||
{/* <Button type='link' iconPosition='end' icon={<RightOutlined />}>去注册</Button> */}
|
||||
<Button type='link' onClick={() => {
|
||||
goRegister && goRegister();
|
||||
}}>{`${t('goRegister')} >`}</Button>
|
||||
</div>)}
|
||||
</div>
|
||||
<div className={Style['loginbox-mobile']} style={{
|
||||
display: loginMode === 'sms' ? 'block' : 'none',
|
||||
|
|
@ -112,7 +133,13 @@ export default function Render(props) {
|
|||
<EmailLogin disabled={disabled} url={url} callback={callback} digit={emailDigit}/>
|
||||
{Tip}
|
||||
</div>
|
||||
{ScanMethods}
|
||||
{(scanOptions?.length > 0 || oauthOptions?.length > 0) ? (<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space split={<Divider type="vertical"/>}>
|
||||
{ScanMethods}
|
||||
{OauthMethods}
|
||||
</Space>
|
||||
</div>) : (<></>)}
|
||||
</>) : (<>
|
||||
{loginMode === 'wechatWeb' &&
|
||||
<div className={Style['loginbox-qrcode']}>
|
||||
|
|
@ -123,7 +150,13 @@ export default function Render(props) {
|
|||
<WechatLoginQrCodeForPublic type="login" oakPath="$login-wechatLogin/qrCode" oakAutoUnmount={true} url={state} size={180}/>
|
||||
{Tip}
|
||||
</div>}
|
||||
{InputMethods}
|
||||
{(inputOptions?.length > 0 || oauthOptions?.length > 0) ? (<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space split={<Divider type="vertical"/>}>
|
||||
{InputMethods}
|
||||
{OauthMethods}
|
||||
</Space>
|
||||
</div>) : (<></>)}
|
||||
</>)}
|
||||
</>)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -41,25 +41,27 @@
|
|||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-password {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-email {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-qrcode {
|
||||
padding: 0 32px;
|
||||
padding: 16px 32px;
|
||||
font-size: 14px;
|
||||
height: 268px;
|
||||
padding-top: 16px;
|
||||
|
||||
&__sociallogin {
|
||||
text-align: center;
|
||||
|
|
@ -126,4 +128,11 @@
|
|||
font-size: 13px;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
&-register {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import { EntityDict } from "../../../oak-app-domain";
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, false, {
|
||||
goLogin: (() => void) | undefined;
|
||||
goBack: (() => void) | undefined;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import { OakPreConditionUnsetException } from "oak-domain/lib/types";
|
||||
import { encryptPasswordSha1 } from "../../../utils/password";
|
||||
export default OakComponent({
|
||||
isList: false,
|
||||
data: {
|
||||
allowRegister: false,
|
||||
loginNameMin: 2,
|
||||
loginNameMax: 8,
|
||||
loginNameNeedVerify: false,
|
||||
loginNameRegexs: [],
|
||||
loginNameTip: '',
|
||||
mode: 'all',
|
||||
pwdMin: 8,
|
||||
pwdMax: 24,
|
||||
pwdNeedVerify: false,
|
||||
pwdRegexs: [],
|
||||
pwdTip: '',
|
||||
},
|
||||
properties: {
|
||||
goLogin: undefined,
|
||||
goBack: undefined,
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
const application = this.features.application.getApplication();
|
||||
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||
const loginNameAP = applicationPassports.find((ele) => ele.passport.type === 'loginName');
|
||||
const loginNameConfig = loginNameAP?.passport?.config;
|
||||
const loginNameMin = loginNameConfig?.min ?? 2;
|
||||
const loginNameMax = loginNameConfig?.max ?? 8;
|
||||
const loginNameNeedVerify = loginNameConfig?.verify;
|
||||
const loginNameRegexs = (loginNameConfig?.regexs && loginNameConfig?.regexs.length > 0) ? loginNameConfig?.regexs : [];
|
||||
const loginNameTip = loginNameConfig?.tip;
|
||||
const allowRegister = !!loginNameConfig?.register;
|
||||
const pwdConfig = application?.system?.config?.Password;
|
||||
const mode = pwdConfig?.mode ?? 'all';
|
||||
const pwdMin = pwdConfig?.min ?? 8;
|
||||
const pwdMax = pwdConfig?.max ?? 24;
|
||||
const pwdNeedVerify = !!pwdConfig?.verify;
|
||||
const pwdRegexs = (pwdConfig?.regexs && pwdConfig?.regexs.length > 0) ? pwdConfig?.regexs : [];
|
||||
const pwdTip = pwdConfig?.tip ?? '';
|
||||
this.setState({
|
||||
allowRegister,
|
||||
loginNameMin,
|
||||
loginNameMax,
|
||||
loginNameNeedVerify,
|
||||
loginNameRegexs,
|
||||
loginNameTip,
|
||||
mode,
|
||||
pwdMin,
|
||||
pwdMax,
|
||||
pwdNeedVerify,
|
||||
pwdRegexs,
|
||||
pwdTip,
|
||||
}, () => this.reRender());
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onConfirm(loginName, password) {
|
||||
const { mode } = this.state;
|
||||
let pwd = password;
|
||||
if (mode === 'sha1') {
|
||||
pwd = encryptPasswordSha1(password);
|
||||
}
|
||||
try {
|
||||
await this.features.cache.exec('registerUserByLoginName', {
|
||||
loginName,
|
||||
password: pwd
|
||||
});
|
||||
this.setMessage({
|
||||
type: 'success',
|
||||
content: '注册成功,请前往登录页登录'
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakPreConditionUnsetException) {
|
||||
this.setMessage({
|
||||
type: 'error',
|
||||
content: err.message
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"navigationBarTitleText": "账号注册",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/** index.wxss **/
|
||||
@import "../../../config/styles/mp/index.less";
|
||||
@import "../../../config/styles/mp/mixins.less";
|
||||
|
||||
.page-body {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background-color: @oak-bg-color-container;
|
||||
.safe-area-inset-bottom();
|
||||
}
|
||||
|
||||
.login-box {
|
||||
padding: 36rpx;
|
||||
min-width: 78vw;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<view class="page-body">
|
||||
<view class="login-box">
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"not allow register": "暂未支持自行注册,请联系管理员为您分配账号!",
|
||||
"registerTitle": "账号注册",
|
||||
"label": {
|
||||
"loginName": "账号",
|
||||
"password": "密码",
|
||||
"rePwd": "密码确认"
|
||||
},
|
||||
"placeholder": {
|
||||
"loginName": "请输入账号",
|
||||
"password": "请输入密码",
|
||||
"rePwd": "请再次输入密码"
|
||||
},
|
||||
"validator": {
|
||||
"loginNameMin": "账号最短长度为%{loginNameMin}位",
|
||||
"loginNameMax": "账号最大长度为%{loginNameMax}位",
|
||||
"loginNameVerify": "当前账号未符合规范",
|
||||
"pwdMin": "密码最短长度为%{pwdMin}位",
|
||||
"pwdMax": "密码最短长度为%{pwdMax}位",
|
||||
"pwdVerify": "当前密码较弱",
|
||||
"pwdDiff": "两次输入的密码不一致,请检查",
|
||||
"noRepwd": "请再次确认密码"
|
||||
},
|
||||
"register": "立即注册",
|
||||
"goLogin": "已有账号?立即登录"
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
export default function Render(props: WebComponentProps<EntityDict, 'token', false, {
|
||||
width: string;
|
||||
allowRegister: boolean;
|
||||
loginNameMin: number;
|
||||
loginNameMax: number;
|
||||
loginNameNeedVerify: boolean;
|
||||
loginNameRegexs: string[];
|
||||
loginNameTip: string;
|
||||
pwdMin: number;
|
||||
pwdMax: number;
|
||||
pwdNeedVerify: boolean;
|
||||
pwdRegexs: string[];
|
||||
pwdTip: string;
|
||||
goLogin: () => void;
|
||||
goBack: () => void;
|
||||
}, {
|
||||
onConfirm: (loginName: string, password: string) => Promise<void>;
|
||||
}>): React.JSX.Element;
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Form, Input, Button, } from 'antd';
|
||||
import { EyeTwoTone, EyeInvisibleOutlined, LeftOutlined, } from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
import Style from './web.module.less';
|
||||
export default function Render(props) {
|
||||
const { data, methods } = props;
|
||||
const { width, allowRegister, loginNameMin, loginNameMax, loginNameNeedVerify, loginNameRegexs, loginNameTip, pwdMin, pwdMax, pwdNeedVerify, pwdRegexs, pwdTip, goLogin, goBack, oakExecuting, oakLoading, } = data;
|
||||
const { t, onConfirm } = methods;
|
||||
const [loginName, setLoginName] = useState('');
|
||||
const [loginNameHelp, setLoginNameHelp] = useState('');
|
||||
const [loginNameStatus, setLoginNameStatus] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [password2, setPassword2] = useState('');
|
||||
const [validateHelp, setValidateHelp] = useState('');
|
||||
const [validateHelp2, setValidateHelp2] = useState('');
|
||||
const [validateStatus, setValidateStatus] = useState('');
|
||||
if (!allowRegister) {
|
||||
return (<div className={classNames(Style['registerbox-wrap'], {
|
||||
[Style['registerbox-wrap__mobile']]: width === 'xs',
|
||||
})}>
|
||||
<div style={{ minHeight: 200, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
{t('not allow register')}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
return (<div className={classNames(Style['registerbox-wrap'], {
|
||||
[Style['registerbox-wrap__mobile']]: width === 'xs',
|
||||
})}>
|
||||
{!!goBack && <LeftOutlined style={{ position: 'absolute', top: 40, left: 32, color: '#555555' }} onClick={() => goBack()}/>}
|
||||
<div className={Style['registerbox-hd']}>
|
||||
<div>{t('registerTitle')}</div>
|
||||
</div>
|
||||
<div className={Style['registerbox-bd']}>
|
||||
<Form style={{ maxWidth: 400 }} layout="vertical">
|
||||
<Form.Item label={t('label.loginName')} name="loginName" tooltip={loginNameTip} help={loginNameHelp} hasFeedback validateStatus={loginNameStatus} rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('placeholder.loginName'),
|
||||
validator: (_, value) => {
|
||||
if (value.length < loginNameMin) {
|
||||
setLoginNameHelp(t('validator.loginNameMin', { loginNameMin }));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
}
|
||||
else if (value.length > loginNameMax) {
|
||||
setLoginNameHelp(t('validator.loginNameMax', { loginNameMax }));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
}
|
||||
else if (!!loginNameNeedVerify && loginNameRegexs && loginNameRegexs.length > 0) {
|
||||
for (const regex of loginNameRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(value)) {
|
||||
setLoginNameHelp(t('validator.loginNameVerify'));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setLoginNameHelp('');
|
||||
setLoginNameStatus('success');
|
||||
}
|
||||
},
|
||||
]}>
|
||||
<Input autoFocus onChange={({ currentTarget }) => setLoginName(currentTarget.value)} placeholder={t('placeholder.loginName')} value={loginName}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('label.password')} name="password" help={validateHelp} tooltip={pwdTip} rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('placeholder.password'),
|
||||
validator: (_, value) => {
|
||||
if (value.length < pwdMin) {
|
||||
setValidateHelp(t('validator.pwdMin', { pwdMin }));
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
}
|
||||
else if (value.length > pwdMax) {
|
||||
setValidateHelp(t('validator.pwdMax', { pwdMax }));
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
}
|
||||
else if (!!pwdNeedVerify && pwdRegexs && pwdRegexs.length > 0) {
|
||||
for (const regex of pwdRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(value)) {
|
||||
setValidateHelp(t('validator.pwdVerify'));
|
||||
setValidateHelp2('');
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (password2) {
|
||||
setValidateHelp('');
|
||||
setValidateHelp2(value === password2
|
||||
? ''
|
||||
: t('validator.pwdDiff'));
|
||||
setValidateStatus(value === password2
|
||||
? 'success'
|
||||
: 'error');
|
||||
}
|
||||
else {
|
||||
setValidateHelp2(t('noRepwd'));
|
||||
setValidateHelp('');
|
||||
setValidateStatus('error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (password2) {
|
||||
setValidateHelp('');
|
||||
setValidateHelp2(value === password2
|
||||
? ''
|
||||
: t('validator.pwdDiff'));
|
||||
setValidateStatus(value === password2
|
||||
? 'success'
|
||||
: 'error');
|
||||
}
|
||||
else {
|
||||
setValidateHelp2(t('noRepwd'));
|
||||
setValidateHelp('');
|
||||
setValidateStatus('error');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
]} hasFeedback validateStatus={validateStatus}>
|
||||
<Input.Password value={password} onChange={(e) => {
|
||||
const strValue = e.target.value;
|
||||
setPassword(strValue);
|
||||
}} iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)} placeholder={t('placeholder.password')}/>
|
||||
</Form.Item>
|
||||
<Form.Item label={t('label.rePwd')} name="passwordConfirm" rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
if (password.length < pwdMin || password.length > pwdMax) {
|
||||
return;
|
||||
}
|
||||
else if (!!pwdNeedVerify && pwdRegexs && pwdRegexs.length > 0) {
|
||||
for (const regex of pwdRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(password)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setValidateHelp2(value === password ? '' : t('validator.pwdDiff'));
|
||||
setValidateStatus(value === password ? 'success' : 'error');
|
||||
}
|
||||
},
|
||||
]} validateTrigger="onChange" help={validateHelp2} validateStatus={validateStatus} hasFeedback>
|
||||
<Input.Password value={password2} onChange={(e) => {
|
||||
const strValue = e.target.value;
|
||||
setPassword2(strValue);
|
||||
}} iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)} placeholder={t('placeholder.rePwd')}/>
|
||||
</Form.Item>
|
||||
<Button block size="large" type="primary" onClick={async () => {
|
||||
await onConfirm(loginName, password);
|
||||
}} disabled={!(loginName && loginNameStatus !== 'error' && password && validateStatus !== 'error') || oakExecuting || oakLoading}>
|
||||
{t('register')}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
{!!goLogin && (<div className={Style['registerbox-login']}>
|
||||
<Button type="link" onClick={() => {
|
||||
goLogin && goLogin();
|
||||
}}>
|
||||
{t('goLogin')}
|
||||
</Button>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
.registerbox {
|
||||
&-main {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background: var(--oak-bg-color-container);
|
||||
}
|
||||
|
||||
&-logo {
|
||||
width: 194px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&-wrap {
|
||||
width: 400px;
|
||||
display: block;
|
||||
background: var(--oak-bg-color-container);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%);
|
||||
transition: all 0.5s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-hd {
|
||||
padding: 32px;
|
||||
padding-bottom: 0px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-bd {
|
||||
padding: 32px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
&-only {
|
||||
padding-top: 32px !important;
|
||||
}
|
||||
|
||||
&-mobile {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-password {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-email {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-qrcode {
|
||||
padding: 16px 32px;
|
||||
font-size: 14px;
|
||||
height: 268px;
|
||||
|
||||
&__sociallogin {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
&__refresh {
|
||||
color: var(--oak-text-color-brand);
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
color: var(--oak-text-color-brand);
|
||||
font-size: 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__iframe {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
// background-color: rgba(0, 0, 0, .04) !important;
|
||||
}
|
||||
|
||||
&-ft {
|
||||
height: 54px;
|
||||
border-top: 1px solid #f2f3f5;
|
||||
font-size: 14px;
|
||||
|
||||
&__btn {}
|
||||
}
|
||||
|
||||
&-protocal {
|
||||
padding: 20px 32px;
|
||||
}
|
||||
|
||||
&-current {
|
||||
color: var(--oak-text-color-brand) !important;
|
||||
cursor: default;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&-methods {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 24px 16px 24px;
|
||||
font-size: 13px;
|
||||
color: #6c7d8f;
|
||||
}
|
||||
|
||||
&-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
&-login {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -685,7 +685,8 @@ const i18ns = [
|
|||
"resendAfter": "秒后可重发",
|
||||
"otherMethods": "其他登录方式",
|
||||
"scanLogin": "扫码登录",
|
||||
"tip": "未注册用户首次登录将自动注册"
|
||||
"tip": "未注册用户首次登录将自动注册",
|
||||
"goRegister": "去注册"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -780,6 +781,39 @@ const i18ns = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "36c643dbcc19c3258f6077a6684236ff",
|
||||
namespace: "oak-general-business-c-user-register",
|
||||
language: "zh-CN",
|
||||
module: "oak-general-business",
|
||||
position: "src/components/user/register",
|
||||
data: {
|
||||
"not allow register": "暂未支持自行注册,请联系管理员为您分配账号!",
|
||||
"registerTitle": "账号注册",
|
||||
"label": {
|
||||
"loginName": "账号",
|
||||
"password": "密码",
|
||||
"rePwd": "密码确认"
|
||||
},
|
||||
"placeholder": {
|
||||
"loginName": "请输入账号",
|
||||
"password": "请输入密码",
|
||||
"rePwd": "请再次输入密码"
|
||||
},
|
||||
"validator": {
|
||||
"loginNameMin": "账号最短长度为%{loginNameMin}位",
|
||||
"loginNameMax": "账号最大长度为%{loginNameMax}位",
|
||||
"loginNameVerify": "当前账号未符合规范",
|
||||
"pwdMin": "密码最短长度为%{pwdMin}位",
|
||||
"pwdMax": "密码最短长度为%{pwdMax}位",
|
||||
"pwdVerify": "当前密码较弱",
|
||||
"pwdDiff": "两次输入的密码不一致,请检查",
|
||||
"noRepwd": "请再次确认密码"
|
||||
},
|
||||
"register": "立即注册",
|
||||
"goLogin": "已有账号?立即登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "5bf96a3e054b8d73c76d7bb45ea90a80",
|
||||
namespace: "oak-general-business-c-userEntityGrant-claim",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "applicationPassport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import oauthProviderTriggers from './oauthProvider';
|
|||
import oauthUserTriggers from './oauthUser';
|
||||
import oauthUserAuthTriggers from './oauthUserAuth';
|
||||
import mobileTriggers from './mobile';
|
||||
import applicationPassportTriggers from './applicationPassport';
|
||||
// import accountTriggers from './account';
|
||||
export default [
|
||||
// ...accountTriggers,
|
||||
|
|
@ -47,4 +48,5 @@ export default [
|
|||
...oauthUserTriggers,
|
||||
...oauthUserAuthTriggers,
|
||||
...mobileTriggers,
|
||||
...applicationPassportTriggers,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -735,5 +735,16 @@ export type AspectDict<ED extends EntityDict> = {
|
|||
setUserAvatarFromWechat: (params: {
|
||||
avatar: string;
|
||||
}, context: BackendRuntimeContext<ED>) => Promise<void>;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param loginName 账号
|
||||
* @param password 密码
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
registerUserByLoginName: (params: {
|
||||
loginName: string;
|
||||
password: string;
|
||||
}, context: BackendRuntimeContext<ED>) => Promise<void>;
|
||||
};
|
||||
export default AspectDict;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { getApplication, signatureJsSDK, uploadWechatMedia, batchGetArticle, get
|
|||
import { updateConfig, updateApplicationConfig, updateStyle } from './config';
|
||||
import { syncMessageTemplate, getMessageType } from './template';
|
||||
import { syncSmsTemplate } from './sms';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword, registerUserByLoginName } from './user';
|
||||
import { createWechatLogin } from './wechatLogin';
|
||||
import { unbindingWechat } from './wechatUser';
|
||||
import { getMpUnlimitWxaCode } from './wechatQrCode';
|
||||
|
|
@ -86,6 +86,7 @@ declare const aspectDict: {
|
|||
createOAuthState: typeof createOAuthState;
|
||||
authorize: typeof authorize;
|
||||
setUserAvatarFromWechat: typeof setUserAvatarFromWechat;
|
||||
registerUserByLoginName: typeof registerUserByLoginName;
|
||||
};
|
||||
export default aspectDict;
|
||||
export { AspectDict } from './AspectDict';
|
||||
|
|
|
|||
|
|
@ -89,5 +89,6 @@ const aspectDict = {
|
|||
createOAuthState: oauth_1.createOAuthState,
|
||||
authorize: oauth_1.authorize,
|
||||
setUserAvatarFromWechat: token_1.setUserAvatarFromWechat,
|
||||
registerUserByLoginName: user_1.registerUserByLoginName,
|
||||
};
|
||||
exports.default = aspectDict;
|
||||
|
|
|
|||
|
|
@ -23,3 +23,12 @@ export declare function updateUserPassword<ED extends EntityDict>(params: {
|
|||
result: string;
|
||||
times?: undefined;
|
||||
}>;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param params
|
||||
* @param context
|
||||
*/
|
||||
export declare function registerUserByLoginName<ED extends EntityDict>(params: {
|
||||
loginName: string;
|
||||
password: string;
|
||||
}, context: BRC<ED>): Promise<void>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.updateUserPassword = exports.getChangePasswordChannels = exports.mergeUser = void 0;
|
||||
exports.mergeUser = mergeUser;
|
||||
exports.getChangePasswordChannels = getChangePasswordChannels;
|
||||
exports.updateUserPassword = updateUserPassword;
|
||||
exports.registerUserByLoginName = registerUserByLoginName;
|
||||
const tslib_1 = require("tslib");
|
||||
const types_1 = require("oak-domain/lib/types");
|
||||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||||
|
|
@ -140,7 +143,6 @@ async function mergeUser(params, context, innerLogic) {
|
|||
}, { dontCollect: true });
|
||||
}
|
||||
}
|
||||
exports.mergeUser = mergeUser;
|
||||
async function getChangePasswordChannels(params, context, innerLogic) {
|
||||
const { userId } = params;
|
||||
const mobileList = await context.select('mobile', {
|
||||
|
|
@ -177,26 +179,22 @@ async function getChangePasswordChannels(params, context, innerLogic) {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
exports.getChangePasswordChannels = getChangePasswordChannels;
|
||||
async function updateUserPassword(params, context, innerLogic) {
|
||||
const { userId, prevPassword, captcha, mobile, newPassword } = params;
|
||||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
const [passport] = await context.select('passport', {
|
||||
const [system] = await context.select('system', {
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
systemId: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId,
|
||||
type: 'password',
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
(0, assert_1.assert)(passport);
|
||||
const config = passport.config;
|
||||
(0, assert_1.assert)(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
const [user] = await context.select('user', {
|
||||
data: {
|
||||
|
|
@ -263,13 +261,13 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
const allowUpdate = mode === 'sha1' ? user.passwordSha1 === prevPassword : user.password === prevPassword; //sha1密文模式判断密文是否相等
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: (0, password_1.encryptPasswordSha1)(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
prevPasswordSha1: (0, password_1.encryptPasswordSha1)(prevPassword),
|
||||
|
|
@ -277,19 +275,19 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: prevPassword,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -298,7 +296,7 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
await context.operate('user', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -312,7 +310,7 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -330,7 +328,7 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
userId,
|
||||
result: 'fail',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -359,13 +357,13 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
dontCollect: true,
|
||||
});
|
||||
if (aliveCaptcha) {
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: (0, password_1.encryptPasswordSha1)(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
|
|
@ -373,19 +371,19 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -393,7 +391,7 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
await context.operate('user', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -407,7 +405,7 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
}, {
|
||||
dontCollect: true,
|
||||
|
|
@ -434,4 +432,83 @@ async function updateUserPassword(params, context, innerLogic) {
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
exports.updateUserPassword = updateUserPassword;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param params
|
||||
* @param context
|
||||
*/
|
||||
async function registerUserByLoginName(params, context) {
|
||||
const { loginName, password } = params;
|
||||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
// 检查loginName是否重复
|
||||
const [existLoginName] = await context.select('loginName', {
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
filter: {
|
||||
name: loginName,
|
||||
ableState: 'enabled',
|
||||
},
|
||||
}, { dontCollect: true, forUpdate: true });
|
||||
if (existLoginName) {
|
||||
closeRootMode();
|
||||
throw new types_1.OakPreConditionUnsetException('账号已存在,请重新设置');
|
||||
}
|
||||
// 创建user并附上密码,级联创建loginName
|
||||
const [system] = await context.select('system', {
|
||||
data: {
|
||||
id: 1,
|
||||
config: 1,
|
||||
},
|
||||
filter: {
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true });
|
||||
(0, assert_1.assert)(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
let passwordData = {};
|
||||
if (mode === 'all') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
passwordSha1: (0, password_1.encryptPasswordSha1)(password),
|
||||
};
|
||||
}
|
||||
else if (mode === 'plain') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
};
|
||||
}
|
||||
else if (mode === 'sha1') {
|
||||
passwordData = {
|
||||
passwordSha1: password,
|
||||
};
|
||||
}
|
||||
const userData = {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
loginName$user: [
|
||||
{
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
name: loginName,
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
Object.assign(userData, passwordData);
|
||||
await context.operate('user', {
|
||||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||||
action: 'create',
|
||||
data: userData,
|
||||
}, {});
|
||||
}
|
||||
catch (err) {
|
||||
closeRootMode();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -687,7 +687,8 @@ const i18ns = [
|
|||
"resendAfter": "秒后可重发",
|
||||
"otherMethods": "其他登录方式",
|
||||
"scanLogin": "扫码登录",
|
||||
"tip": "未注册用户首次登录将自动注册"
|
||||
"tip": "未注册用户首次登录将自动注册",
|
||||
"goRegister": "去注册"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -782,6 +783,39 @@ const i18ns = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "36c643dbcc19c3258f6077a6684236ff",
|
||||
namespace: "oak-general-business-c-user-register",
|
||||
language: "zh-CN",
|
||||
module: "oak-general-business",
|
||||
position: "src/components/user/register",
|
||||
data: {
|
||||
"not allow register": "暂未支持自行注册,请联系管理员为您分配账号!",
|
||||
"registerTitle": "账号注册",
|
||||
"label": {
|
||||
"loginName": "账号",
|
||||
"password": "密码",
|
||||
"rePwd": "密码确认"
|
||||
},
|
||||
"placeholder": {
|
||||
"loginName": "请输入账号",
|
||||
"password": "请输入密码",
|
||||
"rePwd": "请再次输入密码"
|
||||
},
|
||||
"validator": {
|
||||
"loginNameMin": "账号最短长度为%{loginNameMin}位",
|
||||
"loginNameMax": "账号最大长度为%{loginNameMax}位",
|
||||
"loginNameVerify": "当前账号未符合规范",
|
||||
"pwdMin": "密码最短长度为%{pwdMin}位",
|
||||
"pwdMax": "密码最短长度为%{pwdMax}位",
|
||||
"pwdVerify": "当前密码较弱",
|
||||
"pwdDiff": "两次输入的密码不一致,请检查",
|
||||
"noRepwd": "请再次确认密码"
|
||||
},
|
||||
"register": "立即注册",
|
||||
"goLogin": "已有账号?立即登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "5bf96a3e054b8d73c76d7bb45ea90a80",
|
||||
namespace: "oak-general-business-c-userEntityGrant-claim",
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
declare const _default: (import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "message", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "address", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "application", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "applicationPassport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "article", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "articleMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "extraFile", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "user", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "userEntityGrant", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatQrCode", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "notification", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatLogin", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "parasite", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "sessionMessage", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMenu", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatPublicTag", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "wechatMpJump", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "system", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "passport", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthApplication", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthProvider", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUser", import("..").BRC<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "oauthUserAuthorization", import("../context/BackendRuntimeContext").BackendRuntimeContext<import("../oak-app-domain").EntityDict>> | import("oak-domain/lib/types").Trigger<import("../oak-app-domain").EntityDict, "mobile", import("..").BRC<import("../oak-app-domain").EntityDict>>)[];
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const oauthProvider_1 = tslib_1.__importDefault(require("./oauthProvider"));
|
|||
const oauthUser_1 = tslib_1.__importDefault(require("./oauthUser"));
|
||||
const oauthUserAuth_1 = tslib_1.__importDefault(require("./oauthUserAuth"));
|
||||
const mobile_1 = tslib_1.__importDefault(require("./mobile"));
|
||||
const applicationPassport_1 = tslib_1.__importDefault(require("./applicationPassport"));
|
||||
// import accountTriggers from './account';
|
||||
exports.default = [
|
||||
// ...accountTriggers,
|
||||
|
|
@ -50,4 +51,5 @@ exports.default = [
|
|||
...oauthUser_1.default,
|
||||
...oauthUserAuth_1.default,
|
||||
...mobile_1.default,
|
||||
...applicationPassport_1.default,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1008,6 +1008,20 @@ export type AspectDict<ED extends EntityDict> = {
|
|||
},
|
||||
context: BackendRuntimeContext<ED>
|
||||
) => Promise<void>;
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param loginName 账号
|
||||
* @param password 密码
|
||||
* @param context
|
||||
* @returns
|
||||
*/
|
||||
registerUserByLoginName: (
|
||||
params: {
|
||||
loginName: string,
|
||||
password: string,
|
||||
},
|
||||
context: BackendRuntimeContext<ED>
|
||||
) => Promise<void>;
|
||||
};
|
||||
|
||||
export default AspectDict;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import {
|
|||
import { updateConfig, updateApplicationConfig, updateStyle } from './config';
|
||||
import { syncMessageTemplate, getMessageType } from './template';
|
||||
import { syncSmsTemplate } from './sms';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword } from './user';
|
||||
import { mergeUser, getChangePasswordChannels, updateUserPassword, registerUserByLoginName } from './user';
|
||||
import { createWechatLogin } from './wechatLogin';
|
||||
import { unbindingWechat } from './wechatUser';
|
||||
import { getMpUnlimitWxaCode } from './wechatQrCode';
|
||||
|
|
@ -144,6 +144,7 @@ const aspectDict = {
|
|||
createOAuthState,
|
||||
authorize,
|
||||
setUserAvatarFromWechat,
|
||||
registerUserByLoginName,
|
||||
};
|
||||
|
||||
export default aspectDict;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { OakOperationUnpermittedException } from "oak-domain/lib/types";
|
||||
import { OakOperationUnpermittedException, OakPreConditionUnsetException } from "oak-domain/lib/types";
|
||||
import { generateNewIdAsync } from "oak-domain/lib/utils/uuid";
|
||||
import { BackendRuntimeContext } from "../context/BackendRuntimeContext";
|
||||
import { EntityDict } from "../oak-app-domain";
|
||||
|
|
@ -6,7 +6,6 @@ import { encryptPasswordSha1 } from '../utils/password';
|
|||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import dayjs from 'dayjs';
|
||||
import { BRC } from "../types/RuntimeCxt";
|
||||
import { PwdConfig } from "../entities/Passport";
|
||||
|
||||
export async function mergeUser<ED extends EntityDict>(
|
||||
params: {
|
||||
|
|
@ -204,22 +203,19 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
const [passport] = await context.select('passport',
|
||||
const [system] = await context.select('system',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
type: 1,
|
||||
config: 1,
|
||||
systemId: 1,
|
||||
},
|
||||
filter: {
|
||||
systemId,
|
||||
type: 'password',
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true }
|
||||
);
|
||||
assert(passport);
|
||||
const config = passport.config as PwdConfig;
|
||||
assert(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
const [user] = await context.select(
|
||||
'user',
|
||||
|
|
@ -299,31 +295,31 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
}
|
||||
|
||||
const allowUpdate = mode === 'sha1' ? user.passwordSha1 === prevPassword : user.password === prevPassword; //sha1密文模式判断密文是否相等
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
prevPasswordSha1: encryptPasswordSha1(prevPassword),
|
||||
newPasswordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
} else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword,
|
||||
newPassword,
|
||||
};
|
||||
} else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: prevPassword,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -335,7 +331,7 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -353,7 +349,7 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -374,7 +370,7 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'fail',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -409,31 +405,31 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
}
|
||||
);
|
||||
if (aliveCaptcha) {
|
||||
let userDate = {}, changeCreateDate = {};
|
||||
let userData = {}, changeCreateData = {};
|
||||
if (mode === 'all') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
passwordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
newPasswordSha1: encryptPasswordSha1(newPassword),
|
||||
};
|
||||
} else if (mode === 'plain') {
|
||||
userDate = {
|
||||
userData = {
|
||||
password: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPassword: user.password,
|
||||
newPassword,
|
||||
};
|
||||
} else if (mode === 'sha1') {
|
||||
userDate = {
|
||||
userData = {
|
||||
passwordSha1: newPassword,
|
||||
};
|
||||
changeCreateDate = {
|
||||
changeCreateData = {
|
||||
prevPasswordSha1: user.passwordSha1,
|
||||
newPasswordSha1: newPassword,
|
||||
};
|
||||
|
|
@ -443,7 +439,7 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'update',
|
||||
data: userDate,
|
||||
data: userData,
|
||||
filter: {
|
||||
id: userId,
|
||||
},
|
||||
|
|
@ -461,7 +457,7 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
id: await generateNewIdAsync(),
|
||||
userId,
|
||||
result: 'success',
|
||||
...changeCreateDate,
|
||||
...changeCreateData,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -488,3 +484,92 @@ export async function updateUserPassword<ED extends EntityDict>(params: { userId
|
|||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户账号注册
|
||||
* @param params
|
||||
* @param context
|
||||
*/
|
||||
export async function registerUserByLoginName<ED extends EntityDict>(
|
||||
params: {
|
||||
loginName: string,
|
||||
password: string,
|
||||
}, context: BRC<ED>,
|
||||
) {
|
||||
const { loginName, password } = params;
|
||||
const systemId = context.getSystemId();
|
||||
const closeRootMode = context.openRootMode();
|
||||
try {
|
||||
// 检查loginName是否重复
|
||||
const [existLoginName] = await context.select(
|
||||
'loginName',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
name: 1,
|
||||
},
|
||||
filter: {
|
||||
name: loginName!,
|
||||
ableState: 'enabled',
|
||||
},
|
||||
},
|
||||
{ dontCollect: true, forUpdate: true }
|
||||
);
|
||||
if (existLoginName) {
|
||||
closeRootMode();
|
||||
throw new OakPreConditionUnsetException('账号已存在,请重新设置');
|
||||
}
|
||||
// 创建user并附上密码,级联创建loginName
|
||||
const [system] = await context.select('system',
|
||||
{
|
||||
data: {
|
||||
id: 1,
|
||||
config: 1,
|
||||
},
|
||||
filter: {
|
||||
id: systemId,
|
||||
}
|
||||
}, { forUpdate: true }
|
||||
);
|
||||
assert(system);
|
||||
const config = system.config?.Password;
|
||||
const mode = config?.mode ?? 'all';
|
||||
let passwordData = {};
|
||||
if (mode === 'all') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
passwordSha1: encryptPasswordSha1(password),
|
||||
};
|
||||
} else if (mode === 'plain') {
|
||||
passwordData = {
|
||||
password: password,
|
||||
};
|
||||
} else if (mode === 'sha1') {
|
||||
passwordData = {
|
||||
passwordSha1: password,
|
||||
};
|
||||
}
|
||||
const userData: EntityDict['user']['CreateSingle']['data'] = {
|
||||
id: await generateNewIdAsync(),
|
||||
loginName$user: [
|
||||
{
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: {
|
||||
id: await generateNewIdAsync(),
|
||||
name: loginName,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Object.assign(userData, passwordData)
|
||||
await context.operate('user', {
|
||||
id: await generateNewIdAsync(),
|
||||
action: 'create',
|
||||
data: userData,
|
||||
}, {});
|
||||
} catch (err) {
|
||||
closeRootMode();
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { WebConfig, WechatPublicConfig, AppType } from "../../../entities/Application";
|
||||
|
||||
import { LOCAL_STORAGE_KEYS } from '../../../config/constants';
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { EntityDict, } from "../../../oak-app-domain";
|
||||
const LOGIN_MODE = LOCAL_STORAGE_KEYS.loginMode;
|
||||
|
||||
type Option = {
|
||||
|
|
@ -46,6 +46,8 @@ export default OakComponent({
|
|||
url: '', // 登录系统之后要返回的页面
|
||||
callback: undefined as (() => void) | undefined, // 登录成功回调,排除微信登录方式
|
||||
goRegister: undefined as (() => void) | undefined, //跳转注册
|
||||
isRegisterBack: false, //从注册页跳回登录时将优先选中账号登录方式
|
||||
goOauthLogin: undefined as ((oauthProviderId: string) => void) | undefined //跳转指定第三方授权
|
||||
},
|
||||
formData({ features, props }) {
|
||||
return {};
|
||||
|
|
@ -89,9 +91,21 @@ export default OakComponent({
|
|||
// scanOptions,
|
||||
// })
|
||||
// }
|
||||
isRegisterBack(prev, next) {
|
||||
if (prev.isRegisterBack !== next.isRegisterBack && next.isRegisterBack) {
|
||||
const { passportTypes } = this.state;
|
||||
const { onlyCaptcha } = this.props;
|
||||
if (passportTypes.includes('loginName') && !onlyCaptcha) {
|
||||
this.setState({
|
||||
loginMode: 'password'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
const { isRegisterBack } = this.props;
|
||||
const application = this.features.application.getApplication();
|
||||
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||
const defaultPassport = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.isDefault);
|
||||
|
|
@ -148,7 +162,7 @@ export default OakComponent({
|
|||
}
|
||||
});
|
||||
const oauthAp = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'oauth');
|
||||
const { oauthIds } = oauthAp?.config || {};
|
||||
const { oauthIds } = oauthAp?.passport?.config || {};
|
||||
if (oauthIds && oauthIds.length > 0) {
|
||||
const { data: oauthProviders } = await this.features.cache.refresh('oauthProvider', {
|
||||
data: {
|
||||
|
|
@ -173,8 +187,10 @@ export default OakComponent({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!passportTypes.includes(loginMode)) {
|
||||
if (isRegisterBack && !onlyCaptcha) {
|
||||
loginMode = 'password'
|
||||
}
|
||||
if ((loginMode !== 'password' && !passportTypes.includes(loginMode)) || (loginMode === 'password' && !showPassword)) {
|
||||
loginMode = defaultPassport.passport.type;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,5 +13,6 @@
|
|||
"resendAfter": "秒后可重发",
|
||||
"otherMethods": "其他登录方式",
|
||||
"scanLogin": "扫码登录",
|
||||
"tip": "未注册用户首次登录将自动注册"
|
||||
"tip": "未注册用户首次登录将自动注册",
|
||||
"goRegister": "去注册"
|
||||
}
|
||||
|
|
@ -41,25 +41,27 @@
|
|||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-password {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-email {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-qrcode {
|
||||
padding: 0 32px;
|
||||
padding: 16px 32px;
|
||||
font-size: 14px;
|
||||
height: 268px;
|
||||
padding-top: 16px;
|
||||
|
||||
&__sociallogin {
|
||||
text-align: center;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
isPassword,
|
||||
isCaptcha,
|
||||
} from 'oak-domain/lib/utils/validator';
|
||||
import { Form, Input, Button, Checkbox, Typography, Segmented, Divider, Space } from 'antd';
|
||||
import { Form, Input, Button, Checkbox, Typography, Segmented, Divider, Space, Tooltip, Image } from 'antd';
|
||||
import {
|
||||
LockOutlined,
|
||||
MobileOutlined,
|
||||
|
|
@ -15,6 +15,7 @@ import {
|
|||
MailOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
RightOutlined,
|
||||
LinkOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
|
|
@ -59,10 +60,16 @@ export default function Render(
|
|||
allowLoginName: boolean;
|
||||
pwdMode: 'all' | 'plain' | 'sha1';
|
||||
allowRegister: boolean;
|
||||
oauthOptions: {
|
||||
name: string,
|
||||
value: string,
|
||||
logo?: string,
|
||||
}[];
|
||||
goRegister: () => void;
|
||||
goOauthLogin: (oauthProviderId: string) => void;
|
||||
},
|
||||
{
|
||||
setLoginMode: (value: number) => void;
|
||||
goRegister: () => void;
|
||||
}
|
||||
>
|
||||
) {
|
||||
|
|
@ -88,8 +95,11 @@ export default function Render(
|
|||
allowLoginName,
|
||||
pwdMode,
|
||||
allowRegister,
|
||||
oauthOptions,
|
||||
goRegister,
|
||||
goOauthLogin,
|
||||
} = data;
|
||||
const { t, setLoginMode, goRegister } = methods;
|
||||
const { t, setLoginMode, } = methods;
|
||||
|
||||
let redirectUri2 = redirectUri;
|
||||
if (!(redirectUri.startsWith('https') || redirectUri.startsWith('http'))) {
|
||||
|
|
@ -117,49 +127,73 @@ export default function Render(
|
|||
}, [loginMode]);
|
||||
|
||||
const InputMethods = inputOptions && inputOptions.length > 0 ? (
|
||||
<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24 }}>
|
||||
{inputOptions.map((ele) => {
|
||||
let icon = <></>;
|
||||
if (ele.value === 'sms') {
|
||||
icon = <MobileOutlined />;
|
||||
} else if (ele.value === 'password') {
|
||||
icon = <DesktopOutlined />;
|
||||
} else if (ele.value === 'email') {
|
||||
icon = <MailOutlined />;
|
||||
}
|
||||
return (
|
||||
<Space
|
||||
key={ele.value}
|
||||
size={4}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
setLoginMode(ele.value)
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<div>{ele.label}</div>
|
||||
</Space>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24 }}>
|
||||
{inputOptions.map((ele) => {
|
||||
let icon = <></>;
|
||||
if (ele.value === 'sms') {
|
||||
icon = <MobileOutlined />;
|
||||
} else if (ele.value === 'password') {
|
||||
icon = <DesktopOutlined />;
|
||||
} else if (ele.value === 'email') {
|
||||
icon = <MailOutlined />;
|
||||
}
|
||||
return (
|
||||
<Space
|
||||
key={ele.value}
|
||||
size={4}
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
setLoginMode(ele.value)
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
<div>{ele.label}</div>
|
||||
</Space>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
// </div>
|
||||
) : <></>
|
||||
|
||||
const ScanMethods = scanOptions && scanOptions.length > 0 ? (
|
||||
<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
setLoginMode(scanOptions[0].value)
|
||||
}}
|
||||
>
|
||||
<QrcodeOutlined />
|
||||
<div>{t('scanLogin')}</div>
|
||||
</Space>
|
||||
</div>
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => {
|
||||
setLoginMode(scanOptions[0].value)
|
||||
}}
|
||||
>
|
||||
<QrcodeOutlined />
|
||||
<div>{t('scanLogin')}</div>
|
||||
</Space>
|
||||
// </div>
|
||||
) : <></>
|
||||
|
||||
const OauthMethods = oauthOptions && oauthOptions.length > 0 ? (
|
||||
// <div className={Style['loginbox-methods']}>
|
||||
// <Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{oauthOptions?.map((ele) => (
|
||||
<Tooltip title={ele.name}>
|
||||
<Image
|
||||
preview={false}
|
||||
width={20}
|
||||
height={20}
|
||||
src={ele.logo}
|
||||
onClick={() => {
|
||||
goOauthLogin(ele.value)
|
||||
}}
|
||||
placeholder={<LinkOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
))}
|
||||
</Space>
|
||||
// </div>
|
||||
) : <></>
|
||||
|
||||
const Tip = <div className={Style['loginbox-tip']}>
|
||||
|
|
@ -231,7 +265,7 @@ export default function Render(
|
|||
{/* <Button type='link' iconPosition='end' icon={<RightOutlined />}>去注册</Button> */}
|
||||
<Button type='link' onClick={() => {
|
||||
goRegister && goRegister()
|
||||
}}>{`去注册 >`}</Button>
|
||||
}}>{`${t('goRegister')} >`}</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -263,7 +297,15 @@ export default function Render(
|
|||
/>
|
||||
{Tip}
|
||||
</div>
|
||||
{ScanMethods}
|
||||
{(scanOptions?.length > 0 || oauthOptions?.length > 0) ? (
|
||||
<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space split={<Divider type="vertical" />}>
|
||||
{ScanMethods}
|
||||
{OauthMethods}
|
||||
</Space>
|
||||
</div>
|
||||
) : (<></>)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
@ -293,7 +335,15 @@ export default function Render(
|
|||
/>
|
||||
{Tip}
|
||||
</div>}
|
||||
{InputMethods}
|
||||
{(inputOptions?.length > 0 || oauthOptions?.length > 0) ? (
|
||||
<div className={Style['loginbox-methods']}>
|
||||
<Divider plain style={{ fontSize: 13, color: '#808080', }}>{t('otherMethods')}</Divider>
|
||||
<Space split={<Divider type="vertical" />}>
|
||||
{InputMethods}
|
||||
{OauthMethods}
|
||||
</Space>
|
||||
</div>
|
||||
) : (<></>)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"navigationBarTitleText": "账号注册",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/** index.wxss **/
|
||||
@import "../../../config/styles/mp/index.less";
|
||||
@import "../../../config/styles/mp/mixins.less";
|
||||
|
||||
.page-body {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
background-color: @oak-bg-color-container;
|
||||
.safe-area-inset-bottom();
|
||||
}
|
||||
|
||||
.login-box {
|
||||
padding: 36rpx;
|
||||
min-width: 78vw;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { OakPreConditionUnsetException } from "oak-domain/lib/types";
|
||||
import { NameConfig } from "../../../entities/Passport";
|
||||
import { EntityDict } from "../../../oak-app-domain";
|
||||
import { encryptPasswordSha1 } from "../../../utils/password";
|
||||
|
||||
export default OakComponent({
|
||||
isList: false,
|
||||
data: {
|
||||
allowRegister: false,
|
||||
loginNameMin: 2,
|
||||
loginNameMax: 8,
|
||||
loginNameNeedVerify: false,
|
||||
loginNameRegexs: [] as string[],
|
||||
loginNameTip: '',
|
||||
mode: 'all',
|
||||
pwdMin: 8,
|
||||
pwdMax: 24,
|
||||
pwdNeedVerify: false,
|
||||
pwdRegexs: [] as string[],
|
||||
pwdTip: '',
|
||||
},
|
||||
properties: {
|
||||
goLogin: undefined as (() => void) | undefined,
|
||||
goBack: undefined as (() => void) | undefined,
|
||||
},
|
||||
lifetimes: {
|
||||
async ready() {
|
||||
const application = this.features.application.getApplication();
|
||||
|
||||
const { result: applicationPassports } = await this.features.cache.exec('getApplicationPassports', { applicationId: application.id });
|
||||
const loginNameAP = applicationPassports.find((ele: EntityDict['applicationPassport']['Schema']) => ele.passport.type === 'loginName');
|
||||
const loginNameConfig = loginNameAP?.passport?.config as NameConfig;
|
||||
const loginNameMin = loginNameConfig?.min ?? 2;
|
||||
const loginNameMax = loginNameConfig?.max ?? 8;
|
||||
const loginNameNeedVerify = loginNameConfig?.verify;
|
||||
const loginNameRegexs = (loginNameConfig?.regexs && loginNameConfig?.regexs.length > 0) ? loginNameConfig?.regexs : [];
|
||||
const loginNameTip = loginNameConfig?.tip;
|
||||
const allowRegister = !!loginNameConfig?.register;
|
||||
|
||||
const pwdConfig = application?.system?.config?.Password;
|
||||
const mode = pwdConfig?.mode ?? 'all';
|
||||
const pwdMin = pwdConfig?.min ?? 8;
|
||||
const pwdMax = pwdConfig?.max ?? 24;
|
||||
const pwdNeedVerify = !!pwdConfig?.verify;
|
||||
const pwdRegexs = (pwdConfig?.regexs && pwdConfig?.regexs.length > 0) ? pwdConfig?.regexs : [];
|
||||
const pwdTip = pwdConfig?.tip ?? '';
|
||||
this.setState(
|
||||
{
|
||||
allowRegister,
|
||||
loginNameMin,
|
||||
loginNameMax,
|
||||
loginNameNeedVerify,
|
||||
loginNameRegexs,
|
||||
loginNameTip,
|
||||
mode,
|
||||
pwdMin,
|
||||
pwdMax,
|
||||
pwdNeedVerify,
|
||||
pwdRegexs,
|
||||
pwdTip,
|
||||
},
|
||||
() => this.reRender()
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async onConfirm(loginName: string, password: string) {
|
||||
const { mode } = this.state;
|
||||
let pwd = password;
|
||||
if (mode === 'sha1') {
|
||||
pwd = encryptPasswordSha1(password);
|
||||
}
|
||||
try {
|
||||
await this.features.cache.exec('registerUserByLoginName', {
|
||||
loginName,
|
||||
password: pwd
|
||||
});
|
||||
this.setMessage(
|
||||
{
|
||||
type: 'success',
|
||||
content: '注册成功,请前往登录页登录'
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
if (err instanceof OakPreConditionUnsetException) {
|
||||
this.setMessage(
|
||||
{
|
||||
type: 'error',
|
||||
content: err.message
|
||||
}
|
||||
)
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<view class="page-body">
|
||||
<view class="login-box">
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"not allow register": "暂未支持自行注册,请联系管理员为您分配账号!",
|
||||
"registerTitle": "账号注册",
|
||||
"label": {
|
||||
"loginName": "账号",
|
||||
"password": "密码",
|
||||
"rePwd": "密码确认"
|
||||
},
|
||||
"placeholder": {
|
||||
"loginName": "请输入账号",
|
||||
"password": "请输入密码",
|
||||
"rePwd": "请再次输入密码"
|
||||
},
|
||||
"validator": {
|
||||
"loginNameMin": "账号最短长度为%{loginNameMin}位",
|
||||
"loginNameMax": "账号最大长度为%{loginNameMax}位",
|
||||
"loginNameVerify": "当前账号未符合规范",
|
||||
"pwdMin": "密码最短长度为%{pwdMin}位",
|
||||
"pwdMax": "密码最短长度为%{pwdMax}位",
|
||||
"pwdVerify": "当前密码较弱",
|
||||
"pwdDiff": "两次输入的密码不一致,请检查",
|
||||
"noRepwd": "请再次确认密码"
|
||||
},
|
||||
"register": "立即注册",
|
||||
"goLogin": "已有账号?立即登录"
|
||||
}
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
.registerbox {
|
||||
&-main {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background: var(--oak-bg-color-container);
|
||||
}
|
||||
|
||||
&-logo {
|
||||
width: 194px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&-wrap {
|
||||
width: 400px;
|
||||
display: block;
|
||||
background: var(--oak-bg-color-container);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 4px rgb(0 0 0 / 8%), 0 0 4px rgb(0 0 0 / 8%);
|
||||
transition: all 0.5s;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&-hd {
|
||||
padding: 32px;
|
||||
padding-bottom: 0px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&-bd {
|
||||
padding: 32px;
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
&-only {
|
||||
padding-top: 32px !important;
|
||||
}
|
||||
|
||||
&-mobile {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-password {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-email {
|
||||
position: relative;
|
||||
padding: 32px;
|
||||
height: 220px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
&-qrcode {
|
||||
padding: 16px 32px;
|
||||
font-size: 14px;
|
||||
height: 268px;
|
||||
|
||||
&__sociallogin {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
&__refresh {
|
||||
color: var(--oak-text-color-brand);
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
&-icon {
|
||||
color: var(--oak-text-color-brand);
|
||||
font-size: 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&__iframe {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&-input {
|
||||
// background-color: rgba(0, 0, 0, .04) !important;
|
||||
}
|
||||
|
||||
&-ft {
|
||||
height: 54px;
|
||||
border-top: 1px solid #f2f3f5;
|
||||
font-size: 14px;
|
||||
|
||||
&__btn {}
|
||||
}
|
||||
|
||||
&-protocal {
|
||||
padding: 20px 32px;
|
||||
}
|
||||
|
||||
&-current {
|
||||
color: var(--oak-text-color-brand) !important;
|
||||
cursor: default;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&-methods {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0px 24px 16px 24px;
|
||||
font-size: 13px;
|
||||
color: #6c7d8f;
|
||||
}
|
||||
|
||||
&-tip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 13px;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
&-login {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Form, Input, Button, } from 'antd';
|
||||
import { EyeTwoTone, EyeInvisibleOutlined, LeftOutlined, } from '@ant-design/icons';
|
||||
import { WebComponentProps } from 'oak-frontend-base';
|
||||
import { EntityDict } from '../../../oak-app-domain';
|
||||
import classNames from 'classnames';
|
||||
import Style from './web.module.less';
|
||||
|
||||
export default function Render(
|
||||
props: WebComponentProps<
|
||||
EntityDict,
|
||||
'token',
|
||||
false,
|
||||
{
|
||||
width: string;
|
||||
allowRegister: boolean;
|
||||
loginNameMin: number;
|
||||
loginNameMax: number;
|
||||
loginNameNeedVerify: boolean;
|
||||
loginNameRegexs: string[];
|
||||
loginNameTip: string;
|
||||
pwdMin: number;
|
||||
pwdMax: number;
|
||||
pwdNeedVerify: boolean;
|
||||
pwdRegexs: string[];
|
||||
pwdTip: string;
|
||||
goLogin: () => void;
|
||||
goBack: () => void;
|
||||
},
|
||||
{
|
||||
onConfirm: (loginName: string, password: string) => Promise<void>;
|
||||
}
|
||||
>
|
||||
) {
|
||||
const { data, methods } = props;
|
||||
const {
|
||||
width,
|
||||
allowRegister,
|
||||
loginNameMin,
|
||||
loginNameMax,
|
||||
loginNameNeedVerify,
|
||||
loginNameRegexs,
|
||||
loginNameTip,
|
||||
pwdMin,
|
||||
pwdMax,
|
||||
pwdNeedVerify,
|
||||
pwdRegexs,
|
||||
pwdTip,
|
||||
goLogin,
|
||||
goBack,
|
||||
oakExecuting,
|
||||
oakLoading,
|
||||
} = data;
|
||||
const { t, onConfirm } = methods;
|
||||
|
||||
const [loginName, setLoginName] = useState('');
|
||||
const [loginNameHelp, setLoginNameHelp] = useState('');
|
||||
const [loginNameStatus, setLoginNameStatus] = useState('' as '' | "success" | "warning" | "error" | "validating" | undefined);
|
||||
const [password, setPassword] = useState('');
|
||||
const [password2, setPassword2] = useState('');
|
||||
const [validateHelp, setValidateHelp] = useState('');
|
||||
const [validateHelp2, setValidateHelp2] = useState('');
|
||||
const [validateStatus, setValidateStatus] = useState('' as '' | "success" | "warning" | "error" | "validating" | undefined);
|
||||
|
||||
if (!allowRegister) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(Style['registerbox-wrap'], {
|
||||
[Style['registerbox-wrap__mobile']]: width === 'xs',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
style={{ minHeight: 200, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'center' }}
|
||||
>
|
||||
{t('not allow register')}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(Style['registerbox-wrap'], {
|
||||
[Style['registerbox-wrap__mobile']]: width === 'xs',
|
||||
})}
|
||||
>
|
||||
{!!goBack && <LeftOutlined style={{ position: 'absolute', top: 40, left: 32, color:'#555555' }} onClick={() => goBack()} />}
|
||||
<div
|
||||
className={Style['registerbox-hd']}
|
||||
>
|
||||
<div>{t('registerTitle')}</div>
|
||||
</div>
|
||||
<div
|
||||
className={Style['registerbox-bd']}
|
||||
>
|
||||
<Form
|
||||
style={{ maxWidth: 400 }}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item
|
||||
label={t('label.loginName')}
|
||||
name="loginName"
|
||||
tooltip={loginNameTip}
|
||||
help={loginNameHelp}
|
||||
hasFeedback
|
||||
validateStatus={loginNameStatus}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('placeholder.loginName'),
|
||||
validator: (_, value) => {
|
||||
if (value.length < loginNameMin) {
|
||||
setLoginNameHelp(t('validator.loginNameMin', { loginNameMin }));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
} else if (value.length > loginNameMax) {
|
||||
setLoginNameHelp(t('validator.loginNameMax', { loginNameMax }));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
} else if (!!loginNameNeedVerify && loginNameRegexs && loginNameRegexs.length > 0) {
|
||||
for (const regex of loginNameRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(value)) {
|
||||
setLoginNameHelp(t('validator.loginNameVerify'));
|
||||
setLoginNameStatus('error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setLoginNameHelp('')
|
||||
setLoginNameStatus('success');
|
||||
}
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
autoFocus
|
||||
onChange={({ currentTarget }) => setLoginName(currentTarget.value)}
|
||||
placeholder={t('placeholder.loginName')}
|
||||
value={loginName}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('label.password')}
|
||||
name="password"
|
||||
help={validateHelp}
|
||||
tooltip={pwdTip}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t('placeholder.password'),
|
||||
validator: (_, value) => {
|
||||
if (value.length < pwdMin) {
|
||||
setValidateHelp(t('validator.pwdMin', { pwdMin }));
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
} else if (value.length > pwdMax) {
|
||||
setValidateHelp(t('validator.pwdMax', { pwdMax }));
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
} else if (!!pwdNeedVerify && pwdRegexs && pwdRegexs.length > 0) {
|
||||
for (const regex of pwdRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(value)) {
|
||||
setValidateHelp(t('validator.pwdVerify'));
|
||||
setValidateHelp2('')
|
||||
setValidateStatus('error');
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (password2) {
|
||||
setValidateHelp('')
|
||||
setValidateHelp2(
|
||||
value === password2
|
||||
? ''
|
||||
: t('validator.pwdDiff')
|
||||
);
|
||||
setValidateStatus(
|
||||
value === password2
|
||||
? 'success'
|
||||
: 'error'
|
||||
);
|
||||
} else {
|
||||
setValidateHelp2(t('noRepwd'));
|
||||
setValidateHelp('');
|
||||
setValidateStatus('error');
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (password2) {
|
||||
setValidateHelp('')
|
||||
setValidateHelp2(
|
||||
value === password2
|
||||
? ''
|
||||
: t('validator.pwdDiff')
|
||||
);
|
||||
setValidateStatus(
|
||||
value === password2
|
||||
? 'success'
|
||||
: 'error'
|
||||
);
|
||||
} else {
|
||||
setValidateHelp2(t('noRepwd'));
|
||||
setValidateHelp('');
|
||||
setValidateStatus('error');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
]}
|
||||
hasFeedback
|
||||
validateStatus={validateStatus}
|
||||
>
|
||||
<Input.Password
|
||||
value={password}
|
||||
onChange={(e) => {
|
||||
const strValue = e.target.value;
|
||||
setPassword(strValue);
|
||||
}}
|
||||
iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
placeholder={t('placeholder.password')}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('label.rePwd')}
|
||||
name="passwordConfirm"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
validator: (_, value) => {
|
||||
if (password.length < pwdMin || password.length > pwdMax) {
|
||||
return;
|
||||
}
|
||||
else if (!!pwdNeedVerify && pwdRegexs && pwdRegexs.length > 0) {
|
||||
for (const regex of pwdRegexs) {
|
||||
const pattern = new RegExp(regex);
|
||||
if (!pattern.test(password)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
setValidateHelp2(value === password ? '' : t('validator.pwdDiff'))
|
||||
setValidateStatus(value === password ? 'success' : 'error')
|
||||
}
|
||||
},
|
||||
]}
|
||||
validateTrigger="onChange"
|
||||
help={validateHelp2}
|
||||
validateStatus={validateStatus}
|
||||
hasFeedback
|
||||
>
|
||||
<Input.Password
|
||||
value={password2}
|
||||
onChange={(e) => {
|
||||
const strValue = e.target.value;
|
||||
setPassword2(strValue)
|
||||
}}
|
||||
iconRender={(visible) => (visible ? <EyeTwoTone /> : <EyeInvisibleOutlined />)}
|
||||
placeholder={t('placeholder.rePwd')}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Button
|
||||
block
|
||||
size="large"
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
await onConfirm(loginName, password);
|
||||
}}
|
||||
disabled={!(loginName && loginNameStatus !== 'error' && password && validateStatus !== 'error') || oakExecuting || oakLoading}
|
||||
>
|
||||
{t('register')}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
{!!goLogin && (
|
||||
<div className={Style['registerbox-login']}>
|
||||
<Button type="link" onClick={() => {
|
||||
goLogin && goLogin()
|
||||
}}>
|
||||
{t('goLogin')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
||||
|
|
@ -687,7 +687,8 @@ const i18ns: I18n[] = [
|
|||
"resendAfter": "秒后可重发",
|
||||
"otherMethods": "其他登录方式",
|
||||
"scanLogin": "扫码登录",
|
||||
"tip": "未注册用户首次登录将自动注册"
|
||||
"tip": "未注册用户首次登录将自动注册",
|
||||
"goRegister": "去注册"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -782,6 +783,39 @@ const i18ns: I18n[] = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "36c643dbcc19c3258f6077a6684236ff",
|
||||
namespace: "oak-general-business-c-user-register",
|
||||
language: "zh-CN",
|
||||
module: "oak-general-business",
|
||||
position: "src/components/user/register",
|
||||
data: {
|
||||
"not allow register": "暂未支持自行注册,请联系管理员为您分配账号!",
|
||||
"registerTitle": "账号注册",
|
||||
"label": {
|
||||
"loginName": "账号",
|
||||
"password": "密码",
|
||||
"rePwd": "密码确认"
|
||||
},
|
||||
"placeholder": {
|
||||
"loginName": "请输入账号",
|
||||
"password": "请输入密码",
|
||||
"rePwd": "请再次输入密码"
|
||||
},
|
||||
"validator": {
|
||||
"loginNameMin": "账号最短长度为%{loginNameMin}位",
|
||||
"loginNameMax": "账号最大长度为%{loginNameMax}位",
|
||||
"loginNameVerify": "当前账号未符合规范",
|
||||
"pwdMin": "密码最短长度为%{pwdMin}位",
|
||||
"pwdMax": "密码最短长度为%{pwdMax}位",
|
||||
"pwdVerify": "当前密码较弱",
|
||||
"pwdDiff": "两次输入的密码不一致,请检查",
|
||||
"noRepwd": "请再次确认密码"
|
||||
},
|
||||
"register": "立即注册",
|
||||
"goLogin": "已有账号?立即登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "5bf96a3e054b8d73c76d7bb45ea90a80",
|
||||
namespace: "oak-general-business-c-userEntityGrant-claim",
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import oauthProviderTriggers from './oauthProvider';
|
|||
import oauthUserTriggers from './oauthUser';
|
||||
import oauthUserAuthTriggers from './oauthUserAuth';
|
||||
import mobileTriggers from './mobile';
|
||||
import applicationPassportTriggers from './applicationPassport';
|
||||
|
||||
// import accountTriggers from './account';
|
||||
|
||||
|
|
@ -50,4 +51,5 @@ export default [
|
|||
...oauthUserTriggers,
|
||||
...oauthUserAuthTriggers,
|
||||
...mobileTriggers,
|
||||
...applicationPassportTriggers,
|
||||
];
|
||||
|
|
|
|||
Loading…
Reference in New Issue