完善了公众号用户getUserInfo

This commit is contained in:
Xu Chang 2023-02-08 20:49:51 +08:00
parent bd38eb68d6
commit 9116ec3a19
41 changed files with 694 additions and 503 deletions

View File

@ -466,11 +466,96 @@ function loginByMobile(params, context) {
});
}
exports.loginByMobile = loginByMobile;
function setUserInfoFromWechat(user, userInfo, context) {
var _a, _b, _c;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var application, config, nickname, gender, avatar, originalNickname, originalGender, extraFile$entity, updateData, extraFileOperations, _d, _e, _f, _g, _h, _j, _k;
var _l, _m, _o, _p;
return tslib_1.__generator(this, function (_q) {
switch (_q.label) {
case 0:
application = context.getApplication();
config = ((_a = application === null || application === void 0 ? void 0 : application.system) === null || _a === void 0 ? void 0 : _a.config) || ((_c = (_b = application === null || application === void 0 ? void 0 : application.system) === null || _b === void 0 ? void 0 : _b.platform) === null || _c === void 0 ? void 0 : _c.config);
nickname = userInfo.nickname, gender = userInfo.gender, avatar = userInfo.avatar;
originalNickname = user.nickname, originalGender = user.gender, extraFile$entity = user.extraFile$entity;
updateData = {};
if (nickname && nickname !== originalNickname) {
Object.assign(updateData, {
nickname: nickname,
});
}
if (gender && gender !== originalGender) {
Object.assign(updateData, {
gender: gender,
});
}
if (!(avatar && ((extraFile$entity === null || extraFile$entity === void 0 ? void 0 : extraFile$entity.length) === 0 ||
(0, extraFile_1.composeFileUrl)(extraFile$entity[0], config) !== avatar))) return [3 /*break*/, 6];
_l = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 1:
_l.id = _q.sent(),
_l.action = 'create';
_e = (_d = Object).assign;
_m = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 2:
_m.id = _q.sent(),
_m.tag1 = 'avatar',
_m.entity = 'user',
_m.entityId = user.id;
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 3:
extraFileOperations = [
(_l.data = _e.apply(_d, [(_m.objectId = _q.sent(),
_m), (0, extraFile_1.decomposeFileUrl)(avatar)]),
_l)
];
if (!(extraFile$entity.length > 0)) return [3 /*break*/, 5];
_g = (_f = extraFileOperations).push;
_o = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 4:
_g.apply(_f, [(_o.id = _q.sent(),
_o.action = 'remove',
_o.data = {},
_o.filter = {
id: extraFile$entity[0].id,
},
_o)]);
_q.label = 5;
case 5:
Object.assign(updateData, {
extraFile$entity: extraFileOperations,
});
_q.label = 6;
case 6:
if (!(Object.keys(updateData).length > 0)) return [3 /*break*/, 9];
_j = (_h = context).operate;
_k = ['user'];
_p = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 7: return [4 /*yield*/, _j.apply(_h, _k.concat([(_p.id = _q.sent(),
_p.action = 'update',
_p.data = updateData,
_p.filter = {
id: user.id,
},
_p), {}]))];
case 8:
_q.sent();
_q.label = 9;
case 9: return [2 /*return*/];
}
});
});
}
function tryRefreshWechatPublicUserInfo(wechatUserId, context) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _a, wechatUser, accessToken, refreshToken, atExpiredAt, rtExpiredAt, scope, now;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
var _a, wechatUser, application, _b, type, config, appId, appSecret, config2, wechatInstance, accessToken, refreshToken, atExpiredAt, rtExpiredAt, scope, openId, user, now, _c, at2, ate2, s2, _d, _e, _f, _g, nickname, gender, avatar;
var _h;
return tslib_1.__generator(this, function (_j) {
switch (_j.label) {
case 0: return [4 /*yield*/, context.select('wechatUser', {
data: {
id: 1,
@ -479,20 +564,76 @@ function tryRefreshWechatPublicUserInfo(wechatUserId, context) {
atExpiredAt: 1,
rtExpiredAt: 1,
scope: 1,
openId: 1,
user: {
id: 1,
nickname: 1,
gender: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
entity: 1,
entityId: 1,
},
filter: {
tag1: 'avatar',
},
},
},
},
filter: {
id: wechatUserId,
}
}, { dontCollect: true })];
case 1:
_a = tslib_1.__read.apply(void 0, [_b.sent(), 1]), wechatUser = _a[0];
accessToken = wechatUser.accessToken, refreshToken = wechatUser.refreshToken, atExpiredAt = wechatUser.atExpiredAt, rtExpiredAt = wechatUser.rtExpiredAt, scope = wechatUser.scope;
_a = tslib_1.__read.apply(void 0, [_j.sent(), 1]), wechatUser = _a[0];
application = context.getApplication();
_b = application, type = _b.type, config = _b.config;
(0, assert_1.assert)(type === 'wechatPublic' && config.type === 'wechatPublic');
config2 = config;
appId = config2.appId;
appSecret = config2.appSecret;
wechatInstance = oak_external_sdk_1.WechatSDK.getInstance(appId, appSecret, type);
accessToken = wechatUser.accessToken, refreshToken = wechatUser.refreshToken, atExpiredAt = wechatUser.atExpiredAt, rtExpiredAt = wechatUser.rtExpiredAt, scope = wechatUser.scope, openId = wechatUser.openId, user = wechatUser.user;
now = Date.now();
(0, assert_1.assert)(scope.toLowerCase().includes('userinfo'));
if (rtExpiredAt < now) {
// refreshToken过期直接返回失败
throw new Exception_1.OakWechatPublicNeedReloginException();
// refreshToken过期直接返回未登录异常,使用户去重新登录
throw new types_1.OakUnloggedInException();
}
if (!(atExpiredAt < now)) return [3 /*break*/, 5];
return [4 /*yield*/, wechatInstance.refreshUserAccessToken(refreshToken)];
case 2:
_c = _j.sent(), at2 = _c.accessToken, ate2 = _c.atExpiredAt, s2 = _c.scope;
_e = (_d = context).operate;
_f = ['wechatUser'];
_h = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 3: return [4 /*yield*/, _e.apply(_d, _f.concat([(_h.id = _j.sent(),
_h.action = 'update',
_h.data = {
accessToken: at2,
atExpiredAt: ate2,
scope: s2,
},
_h), { dontCollect: true, dontCreateModi: true, dontCreateOper: true }]))];
case 4:
_j.sent();
accessToken = at2;
_j.label = 5;
case 5: return [4 /*yield*/, wechatInstance.getUserInfo(accessToken, openId)];
case 6:
_g = _j.sent(), nickname = _g.nickname, gender = _g.gender, avatar = _g.avatar;
return [4 /*yield*/, setUserInfoFromWechat(user, { nickname: nickname, gender: gender, avatar: avatar }, context)];
case 7:
_j.sent();
return [2 /*return*/];
}
});
@ -1241,10 +1382,9 @@ function syncUserInfoWechatMp(_a, context) {
var _b, _c, _d;
var nickname = _a.nickname, avatarUrl = _a.avatarUrl, encryptedData = _a.encryptedData, iv = _a.iv, signature = _a.signature;
return tslib_1.__awaiter(this, void 0, void 0, function () {
var userId, application, config, _e, _f, sessionKey, user, _g, originNickname, extraFile$entity, updateData, extraFileOperations, _h, _j, _k, _l, _m, _o, _p;
var _q, _r, _s, _t;
return tslib_1.__generator(this, function (_u) {
switch (_u.label) {
var userId, application, config, _e, _f, sessionKey, user;
return tslib_1.__generator(this, function (_g) {
switch (_g.label) {
case 0:
userId = context.getToken().userId;
application = context.getApplication();
@ -1258,6 +1398,7 @@ function syncUserInfoWechatMp(_a, context) {
user: {
id: 1,
nickname: 1,
gender: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
@ -1285,73 +1426,29 @@ function syncUserInfoWechatMp(_a, context) {
dontCollect: true,
})];
case 1:
_e = tslib_1.__read.apply(void 0, [_u.sent(), 1]), _f = _e[0], sessionKey = _f.sessionKey, user = _f.user;
_g = user, originNickname = _g.nickname, extraFile$entity = _g.extraFile$entity;
updateData = {};
if (nickname !== originNickname) {
Object.assign(updateData, {
nickname: nickname,
});
}
if (!((extraFile$entity === null || extraFile$entity === void 0 ? void 0 : extraFile$entity.length) === 0 ||
(0, extraFile_1.composeFileUrl)(extraFile$entity[0], config) !== avatarUrl)) return [3 /*break*/, 7];
_q = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
_e = tslib_1.__read.apply(void 0, [_g.sent(), 1]), _f = _e[0], sessionKey = _f.sessionKey, user = _f.user;
// console.log(avatarUrl);
// const { type, config } = application;
// assert(type === 'wechatMp' || config.type === 'wechatMp');
// const config2 = config as WechatMpConfig;
// const { appId, appSecret } = config2;
// const wechatInstance = WechatSDK.getInstance(appId, appSecret, 'wechatMp');
// const result = wechatInstance.decryptData(sessionKey as string, encryptedData, iv, signature);
// 实测发现解密出来的和userInfo完全一致……
// console.log(result);
return [4 /*yield*/, setUserInfoFromWechat(user, { nickname: nickname, avatar: avatarUrl }, context)];
case 2:
_q.id = _u.sent(),
_q.action = 'create';
_j = (_h = Object).assign;
_r = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 3:
_r.id = _u.sent(),
_r.tag1 = 'avatar',
_r.entity = 'user',
_r.entityId = userId;
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 4:
extraFileOperations = [
(_q.data = _j.apply(_h, [(_r.objectId = _u.sent(),
_r), (0, extraFile_1.decomposeFileUrl)(avatarUrl)]),
_q)
];
if (!(extraFile$entity.length > 0)) return [3 /*break*/, 6];
_l = (_k = extraFileOperations).push;
_s = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 5:
_l.apply(_k, [(_s.id = _u.sent(),
_s.action = 'remove',
_s.data = {},
_s.filter = {
id: extraFile$entity[0].id,
},
_s)]);
_u.label = 6;
case 6:
Object.assign(updateData, {
extraFile$entity: extraFileOperations,
});
_u.label = 7;
case 7:
if (!(Object.keys(updateData).length > 0)) return [3 /*break*/, 10];
_o = (_m = context).operate;
_p = ['user'];
_t = {};
return [4 /*yield*/, (0, uuid_1.generateNewIdAsync)()];
case 8: return [4 /*yield*/, _o.apply(_m, _p.concat([(_t.id = _u.sent(),
_t.action = 'update',
_t.data = updateData,
_t.filter = {
id: userId,
},
_t), {
dontCollect: true,
}]))];
case 9:
_u.sent();
_u.label = 10;
case 10: return [2 /*return*/];
// console.log(avatarUrl);
// const { type, config } = application;
// assert(type === 'wechatMp' || config.type === 'wechatMp');
// const config2 = config as WechatMpConfig;
// const { appId, appSecret } = config2;
// const wechatInstance = WechatSDK.getInstance(appId, appSecret, 'wechatMp');
// const result = wechatInstance.decryptData(sessionKey as string, encryptedData, iv, signature);
// 实测发现解密出来的和userInfo完全一致……
// console.log(result);
_g.sent();
return [2 /*return*/];
}
});
});

View File

@ -28,25 +28,29 @@ function Grant(props) {
if (dev) {
V = ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev") }, { children: [(0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_header") }, { children: [(0, jsx_runtime_1.jsx)("input", { maxLength: 6, value: code, className: "".concat(prefixCls2, "_dev_header_input"), onChange: function (e) {
setCode(e.target.value);
} }), (0, jsx_runtime_1.jsx)("button", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_header_btn"), onClick: function () {
} }), (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "primary", shape: "round", size: "large",
// block
onClick: function () {
if (disabled) {
messageApi.info(disableText || '禁用');
messageApi.info(disableText || 'disabled');
return;
}
window.location.href =
decodeURIComponent(redirectUri) +
"?code=".concat(code, "&state=").concat(state);
} }, { children: "\u5FAE\u4FE1\u767B\u5F55" }))] })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom") }, { children: [(0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom_title") }, { children: "\u6A21\u62DF\u5FAE\u4FE1\u6388\u6743\u767B\u5F55" })), (0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom_desc") }, { children: "1\u3001\u7531\u4E8E\u672C\u5730\u5F00\u53D1\u73AF\u5883\u9650\u5236\uFF0C\u6A21\u62DF\u5FAE\u4FE1\u6388\u6743\u540E\u52A8\u4F5C" })), (0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom_desc") }, { children: "2\u3001CODE\u53EF\u4FEE\u6539" }))] }))] })));
} }, { children: "\u5FAE\u4FE1\u6388\u6743\u4E00\u952E\u767B\u5F55" }))] })), (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom") }, { children: [(0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom_desc") }, { children: "1\u3001\u7531\u4E8E\u672C\u5730\u5F00\u53D1\u73AF\u5883\u9650\u5236\uFF0C\u6A21\u62DF\u5FAE\u4FE1\u6388\u6743\u540E\u52A8\u4F5C" })), (0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_dev_bottom_desc") }, { children: "2\u3001CODE\u53EF\u4FEE\u6539" }))] }))] })));
}
else {
V = ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod") }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod_header") }, { children: (0, jsx_runtime_1.jsx)("button", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod_header_btn"), onClick: function () {
if (disabled) {
messageApi.info(disableText || '禁用');
return;
}
var url = WeChatLoginUrl(redirectUri, appId, scope, state);
window.location.href = url;
} }, { children: "\u5FAE\u4FE1\u767B\u5F55" })) })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod_bottom") }, { children: (0, jsx_runtime_1.jsx)("span", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod_bottom_title") }, { children: "\u5FAE\u4FE1\u6388\u6743\u767B\u5F55" })) }))] })));
V = ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod") }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: "".concat(prefixCls2, "_prod_header") }, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "primary", shape: "round", size: "large",
// block
onClick: function () {
if (disabled) {
messageApi.info(disableText || 'disabled');
return;
}
var url = WeChatLoginUrl(redirectUri, appId, scope, state);
window.location.href = url;
} }, { children: "\u5FAE\u4FE1\u6388\u6743\u4E00\u952E\u767B\u5F55" })) })) })));
}
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [contextHolder, (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: prefixCls2, id: id }, { children: V }))] }));
}

View File

@ -1,8 +1,6 @@
.oak-loginGrant {
&_dev {
height: 280px;
border: 1px dashed var(--oak-color-primary);
padding: 10px;
height: 180px;
&_header {
display: flex;
@ -42,31 +40,7 @@
box-shadow: 0 2px #00000004;
cursor: pointer;
transition: .3s;
}
&_btn:hover {
color: var(--oak-color-primary);
border-color: currentColor;
}
&_btn::after {
/*其他样式*/
opacity: 0;
box-shadow: 0 0 0 6px var(--oak-color-primary);
transition: .3s;
}
/*点击*/
&_btn:active::after {
box-shadow: none;
opacity: 0.4;
transition: 0s;
/*取消过渡*/
}
}
@ -93,9 +67,7 @@
}
&_prod {
height: 280px;
border: 1px dashed var(--oak-color-primary);
padding: 10px;
height: 180px;
display: flex;
flex-direction: column;

View File

@ -1,4 +1,4 @@
import { String, Text, Image, Datetime } from 'oak-domain/lib/types/DataType';
import { String, Text, Datetime } from 'oak-domain/lib/types/DataType';
import { Schema as ExtraFile } from './ExtraFile';
import { Schema as WechatQrCode } from './WechatQrCode';
import { EntityShape } from 'oak-domain/lib/types/Entity';
@ -9,7 +9,6 @@ export interface Schema extends EntityShape {
passwordSha1?: Text;
birth?: Datetime;
gender?: 'male' | 'female';
avatar?: Image;
idCardType?: 'ID-Card' | 'passport' | 'Mainland-passport';
idNumber?: String<32>;
ref?: Schema;

View File

@ -64,7 +64,6 @@ var locale = {
password: '密码',
passwordSha1: 'sha1加密密码',
gender: '性别',
avatar: '头像',
idCardType: '证件类型',
idNumber: '证件号码',
ref: '指向用户',

View File

@ -29,4 +29,5 @@ export declare class Token<ED extends EntityDict, Cxt extends BackendRuntimeCont
isReallyRoot(): boolean;
sendCaptcha(mobile: string): Promise<string>;
switchTo(userId: string): Promise<void>;
refreshWechatPublicUserInfo(): Promise<void>;
}

View File

@ -238,6 +238,19 @@ var Token = /** @class */ (function (_super) {
});
});
};
Token.prototype.refreshWechatPublicUserInfo = function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.cache.exec('refreshWechatPublicUserInfo', {})];
case 1:
_a.sent();
this.publish();
return [2 /*return*/];
}
});
});
};
return Token;
}(Feature_1.Feature));
exports.Token = Token;

View File

@ -1,4 +1,4 @@
import { String, Text, Datetime, Image, ForeignKey } from "oak-domain/lib/types/DataType";
import { String, Text, Datetime, ForeignKey } from "oak-domain/lib/types/DataType";
import { Q_DateValue, Q_StringValue, Q_EnumValue, NodeId, MakeFilter, FulltextFilter, ExprOp, ExpressionKey } from "oak-domain/lib/types/Demand";
import { OneOf } from "oak-domain/lib/types/Polyfill";
import * as SubQuery from "../_SubQuery";
@ -26,7 +26,6 @@ export declare type OpSchema = EntityShape & {
passwordSha1?: Text | null;
birth?: Datetime | null;
gender?: ('male' | 'female') | null;
avatar?: Image | null;
idCardType?: ('ID-Card' | 'passport' | 'Mainland-passport') | null;
idNumber?: String<32> | null;
refId?: ForeignKey<"user"> | null;
@ -41,7 +40,6 @@ export declare type Schema = EntityShape & {
passwordSha1?: Text | null;
birth?: Datetime | null;
gender?: ('male' | 'female') | null;
avatar?: Image | null;
idCardType?: ('ID-Card' | 'passport' | 'Mainland-passport') | null;
idNumber?: String<32> | null;
refId?: ForeignKey<"user"> | null;
@ -96,7 +94,6 @@ declare type AttrFilter = {
passwordSha1: Q_StringValue;
birth: Q_DateValue;
gender: Q_EnumValue<'male' | 'female'>;
avatar: Q_StringValue;
idCardType: Q_EnumValue<'ID-Card' | 'passport' | 'Mainland-passport'>;
idNumber: Q_StringValue;
refId: Q_StringValue | SubQuery.UserIdSubQuery;
@ -118,7 +115,6 @@ export declare type Projection = {
passwordSha1?: number;
birth?: number;
gender?: number;
avatar?: number;
idCardType?: number;
idNumber?: number;
refId?: number;
@ -252,8 +248,6 @@ export declare type SortAttr = {
birth: number;
} | {
gender: number;
} | {
avatar: number;
} | {
idCardType: number;
} | {

View File

@ -32,9 +32,6 @@ exports.desc = {
length: 24
}
},
avatar: {
type: "text"
},
idCardType: {
type: "varchar",
params: {

View File

@ -1 +1 @@
{ "attr": { "name": "姓名", "nickname": "昵称", "birth": "生日", "password": "密码", "passwordSha1": "sha1加密密码", "gender": "性别", "avatar": "头像", "idCardType": "证件类型", "idNumber": "证件号码", "ref": "指向用户", "files": "相关文件", "userState": "用户状态", "idState": "身份验证状态", "codes": "微信分享二维码" }, "action": { "activate": "激活", "accept": "同意", "verify": "验证", "reject": "拒绝", "enable": "启用", "disable": "禁用", "mergeTo": "合并", "mergeFrom": "使合并" }, "v": { "userState": { "shadow": "未激活", "normal": "正常", "disabled": "禁用", "merged": "已被合并" }, "idState": { "unverified": "未验证", "verifying": "验证中", "verified": "已验证" }, "gender": { "male": "男", "female": "女" }, "idCardType": { "ID-Card": "身份证", "passport": "护照", "Mainland-passport": "港澳台通行证" } } }
{ "attr": { "name": "姓名", "nickname": "昵称", "birth": "生日", "password": "密码", "passwordSha1": "sha1加密密码", "gender": "性别", "idCardType": "证件类型", "idNumber": "证件号码", "ref": "指向用户", "files": "相关文件", "userState": "用户状态", "idState": "身份验证状态", "codes": "微信分享二维码" }, "action": { "activate": "激活", "accept": "同意", "verify": "验证", "reject": "拒绝", "enable": "启用", "disable": "禁用", "mergeTo": "合并", "mergeFrom": "使合并" }, "v": { "userState": { "shadow": "未激活", "normal": "正常", "disabled": "禁用", "merged": "已被合并" }, "idState": { "unverified": "未验证", "verifying": "验证中", "verified": "已验证" }, "gender": { "male": "男", "female": "女" }, "idCardType": { "ID-Card": "身份证", "passport": "护照", "Mainland-passport": "港澳台通行证" } } }

View File

@ -19,6 +19,7 @@ exports.default = OakComponent({
onlyCaptcha: Boolean,
onlyPassword: Boolean,
eventLoggedIn: String,
callback: Function,
},
formData: function (_a) {
var _this = this;
@ -41,13 +42,15 @@ exports.default = OakComponent({
};
},
methods: {
onInput: function (e) {
var _a;
var _b = this.resolveInput(e), dataset = _b.dataset, value = _b.value;
var attr = dataset.attr;
this.setState((_a = {},
_a[attr] = value,
_a));
setMobile: function (value) {
this.setState({
mobile: value,
});
},
setCaptcha: function (value) {
this.setState({
captcha: value,
});
},
sendCaptcha: function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
@ -84,19 +87,22 @@ exports.default = OakComponent({
},
loginByMobile: function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var eventLoggedIn, _a, mobile, password, captcha, err_2;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
var _a, eventLoggedIn, callback, _b, mobile, password, captcha, err_2;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
eventLoggedIn = this.props.eventLoggedIn;
_a = this.state, mobile = _a.mobile, password = _a.password, captcha = _a.captcha;
_b.label = 1;
_a = this.props, eventLoggedIn = _a.eventLoggedIn, callback = _a.callback;
_b = this.state, mobile = _b.mobile, password = _b.password, captcha = _b.captcha;
_c.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
_c.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.token.loginByMobile(mobile, password, captcha)];
case 2:
_b.sent();
if (eventLoggedIn) {
_c.sent();
if (typeof callback === 'function') {
callback();
}
else if (eventLoggedIn) {
this.pub(eventLoggedIn);
}
else {
@ -104,7 +110,7 @@ exports.default = OakComponent({
}
return [3 /*break*/, 4];
case 3:
err_2 = _b.sent();
err_2 = _c.sent();
this.setMessage({
type: 'error',
content: err_2.message,
@ -114,6 +120,6 @@ exports.default = OakComponent({
}
});
});
}
},
},
});

View File

@ -1,5 +1,5 @@
{
"navigationBarTitleText": "手机号登录",
"navigationBarTitleText": "绑定手机号",
"usingComponents": {
"l-button": "../../../miniprogram_npm/lin-ui/button/index"
}

View File

@ -1,5 +1,5 @@
{
"Login": "进入",
"Login": "确定",
"Send": "发送验证码",
"placeholder": {
"Captcha": "输入4位短信验证码",

View File

@ -0,0 +1,35 @@
.loginbox-main {
height: 100vh;
display: flex;
flex: 1;
align-items: center;
flex-direction: column;
background: var(--oak-bg-color-container);
padding-top: 20%;
}
.loginbox-wrap {
width: 90%;
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%);
}
.loginbox-hd {
padding: 32px;
font-size: 14px;
font-weight: 500;
}
.loginbox-bd {
height: 200px;
}
.loginbox-mobile {
position: relative;
padding: 0 32px;
}

View File

@ -13,6 +13,8 @@ export default function render(props: WebComponentProps<EntityDict, 'token', fal
captcha: string;
password: string;
}, {
sendCaptcha: (mobile: string) => Promise<void>;
loginByMobile: (mobile: string, password?: string, captcha?: string) => Promise<void>;
}>): null;
setCaptcha: (mobile: string) => void;
setMobile: (mobile: string) => void;
sendCaptcha: () => Promise<void>;
loginByMobile: () => Promise<void>;
}>): JSX.Element;

View File

@ -1,100 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var validator_1 = require("oak-domain/lib/utils/validator");
var icons_1 = require("@ant-design/icons");
var antd_1 = require("antd");
var mobile_module_less_1 = tslib_1.__importDefault(require("./mobile.module.less"));
function render(props) {
/* const { mobile, captcha, password, counter } = props.data;
const validMobile = isMobile(mobile);
const validCaptcha = isCaptcha(captcha);
const allowSubmit = validMobile && validCaptcha;
const LoginCaptcha = (
<Form colon={true}>
<Form.Item name="mobile">
<Input
allowClear
value={mobile}
data-attr="mobile"
type="tel"
maxLength={11}
prefix={<MailOutlined />}
placeholder={this.t('placeholder.Mobile')}
size="large"
onChange={(e) => {
this.setState({
mobile: e.target.value,
});
}}
className={Style['loginbox-input']}
/>
</Form.Item>
<Form.Item name="captcha">
<Input
allowClear
value={captcha}
data-attr="captcha"
var _a = props.data, mobile = _a.mobile, captcha = _a.captcha, password = _a.password, counter = _a.counter;
var _b = props.methods, t = _b.t, setMobile = _b.setMobile, setCaptcha = _b.setCaptcha, sendCaptcha = _b.sendCaptcha, loginByMobile = _b.loginByMobile;
var validMobile = (0, validator_1.isMobile)(mobile);
var validCaptcha = (0, validator_1.isCaptcha)(captcha);
var allowSubmit = validMobile && validCaptcha;
var LoginCaptcha = ((0, jsx_runtime_1.jsxs)(antd_1.Form, tslib_1.__assign({ colon: true }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { allowClear: true, value: mobile, "data-attr": "mobile", type: "tel", maxLength: 11, prefix: (0, jsx_runtime_1.jsx)(icons_1.MobileOutlined, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (e) {
setMobile(e.target.value);
}, className: mobile_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "captcha" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { allowClear: true, value: captcha, "data-attr": "captcha",
// type="number"
maxLength={4}
placeholder={this.t('placeholder.Captcha')}
size="large"
onChange={(e) => {
this.setState({
captcha: e.target.value,
});
}}
className={Style['loginbox-input']}
suffix={
<Button
type="link"
disabled={!validMobile || counter > 0}
onClick={() => this.sendCaptcha()}
>
{counter > 0
? `${counter}秒后可重发`
: this.t('Send')}
</Button>
}
/>
</Form.Item>
<Form.Item>
<Button
block
size="large"
type="primary"
htmlType="submit"
disabled={!allowSubmit}
onClick={() => this.loginByMobile()}
>
{this.t('Login')}
</Button>
</Form.Item>
</Form>
);
return (
<div className={Style['loginbox-main']}>
<div className={Style['loginbox-wrap']}>
<div className={Style['loginbox-hd']}>
为了更好的体验请完善账号信息
</div>
<div className={Style['loginbox-bd']}>
<div
className={Style['loginbox-mobile']}
>
{LoginCaptcha}
</div>
</div>
<div className={Style['loginbox-ft']}>
<div className={Style['loginbox-ft__btn']}>
<div className={Style['loginbox-protocal']}>
<Checkbox>
<div>阅读并同意 服务条款 隐私政策</div>
</Checkbox>
</div>
</div>
</div>
</div>
</div>
); */
return null;
maxLength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: function (e) {
setCaptcha(e.target.value);
}, className: mobile_module_less_1.default['loginbox-input'], suffix: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "link", disabled: !validMobile || counter > 0, onClick: function () { return sendCaptcha(); } }, { children: counter > 0 ? "".concat(counter, "\u79D2\u540E\u53EF\u91CD\u53D1") : t('Send') })) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ block: true, size: "large", type: "primary", htmlType: "submit", disabled: !allowSubmit, onClick: function () { return loginByMobile(); } }, { children: t('Login') })) })] })));
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: mobile_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: mobile_module_less_1.default['loginbox-wrap'] }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: mobile_module_less_1.default['loginbox-hd'] }, { children: "\u4E3A\u4E86\u66F4\u597D\u7684\u4F53\u9A8C\uFF0C\u8BF7\u7ED1\u5B9A\u624B\u673A\u53F7" })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: mobile_module_less_1.default['loginbox-bd'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: mobile_module_less_1.default['loginbox-mobile'] }, { children: LoginCaptcha })) }))] })) })));
}
exports.default = render;

View File

@ -1,7 +1,6 @@
.loginbox-main {
height: 100vh;
display: flex;
flex: 1;
align-items: center;
@ -21,26 +20,12 @@
.loginbox-hd {
padding: 32px;
&__tab {
width: 100%;
height: 38px;
background-color: rgba(0, 0, 0, .04) !important;
}
&__tabcon {
width: 100%;
display: flex;
justify-content: center;
}
font-size: 14px;
font-weight: 500;
}
.loginbox-bd {
height: 310px;
}
.loginbox-only {
padding-top: 32px !important;
height: 200px;
}
.loginbox-mobile {
@ -48,64 +33,5 @@
padding: 0 32px;
}
.loginbox-password {
position: relative;
padding: 0 32px;
}
.loginbox-qrcode {
padding: 0 32px;
font-size: 14px;
&__sociallogin {
margin-bottom: 15px;
text-align: center;
color: #999;
}
&__refresh {
color: var(--oak-text-color-brand);
margin-left: 10px;
cursor: pointer;
&-icon {
color: var(--oak-text-color-brand)
}
}
&__iframe {
position: relative;
width: 160px;
height: 160px;
margin: 0 auto;
border: #999 solid 1px;
}
}
.current {
color: var(--oak-text-color-brand) !important;
cursor: default;
background-color: #fff;
border-radius: 4px;
}
.loginbox-input {
.t-input {
background-color: rgba(0, 0, 0, .04) !important;
}
}
.loginbox-ft {
height: 54px;
border-top: 1px solid #f2f3f5;
font-size: 14px;
&__btn {}
}
.loginbox-protocal {
padding: 20px 32px;
}

19
lib/pages/mobile/login/web.pc.d.ts vendored Normal file
View File

@ -0,0 +1,19 @@
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../general-app-domain';
export default function render(props: WebComponentProps<EntityDict, 'token', false, {
counter: number;
loginMode?: number;
loginAgreed?: boolean;
appId: string;
onlyCaptcha?: boolean;
onlyPassword?: boolean;
loading: boolean;
backUrl?: string;
mobile: string;
captcha: string;
}, {
setCaptcha: (mobile: string) => void;
setMobile: (mobile: string) => void;
sendCaptcha: () => Promise<void>;
loginByMobile: () => Promise<void>;
}>): JSX.Element;

View File

@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var validator_1 = require("oak-domain/lib/utils/validator");
var icons_1 = require("@ant-design/icons");
var antd_1 = require("antd");
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
function render(props) {
var _a = props.data, mobile = _a.mobile, captcha = _a.captcha, counter = _a.counter;
var _b = props.methods, t = _b.t, setMobile = _b.setMobile, setCaptcha = _b.setCaptcha, sendCaptcha = _b.sendCaptcha, loginByMobile = _b.loginByMobile;
var validMobile = (0, validator_1.isMobile)(mobile);
var validCaptcha = (0, validator_1.isCaptcha)(captcha);
var allowSubmit = validMobile && validCaptcha;
var LoginCaptcha = ((0, jsx_runtime_1.jsxs)(antd_1.Form, tslib_1.__assign({ colon: true }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "mobile" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { allowClear: true, value: mobile, "data-attr": "mobile", type: "tel", maxLength: 11, prefix: (0, jsx_runtime_1.jsx)(icons_1.MobileOutlined, {}), placeholder: t('placeholder.Mobile'), size: "large", onChange: function (e) {
setMobile(e.target.value);
}, className: web_module_less_1.default['loginbox-input'] }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "captcha" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { allowClear: true, value: captcha, "data-attr": "captcha",
// type="number"
maxLength: 4, placeholder: t('placeholder.Captcha'), size: "large", onChange: function (e) {
setCaptcha(e.target.value);
}, className: web_module_less_1.default['loginbox-input'], suffix: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "link", disabled: !validMobile || counter > 0, onClick: function () { return sendCaptcha(); } }, { children: counter > 0 ? "".concat(counter, "\u79D2\u540E\u53EF\u91CD\u53D1") : t('Send') })) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ block: true, size: "large", type: "primary", htmlType: "submit", disabled: !allowSubmit, onClick: function () { return loginByMobile(); } }, { children: t('Login') })) })] })));
return ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-main'] }, { children: (0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-wrap'] }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-hd'] }, { children: "\u4E3A\u4E86\u66F4\u597D\u7684\u4F53\u9A8C\uFF0C\u8BF7\u7ED1\u5B9A\u624B\u673A\u53F7" })), (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-bd'] }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default['loginbox-mobile'] }, { children: LoginCaptcha })) }))] })) })));
}
exports.default = render;

View File

@ -10,7 +10,8 @@ exports.default = OakComponent({
mobile: 1,
userId: 1,
},
filters: [{
filters: [
{
filter: function () {
var token = this.features.token.getToken();
return {
@ -28,7 +29,8 @@ exports.default = OakComponent({
},
};
},
}],
},
],
formData: function (_a) {
var mobiles = _a.data;
return {
@ -41,6 +43,9 @@ exports.default = OakComponent({
refreshing: false,
deleteIdx: undefined,
},
properties: {
showBack: Boolean,
},
methods: {
onRefreshMobile: function (e) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
@ -57,7 +62,8 @@ exports.default = OakComponent({
title: '获取手机号失败',
type: 'warning',
});
} else {
}
else {
(0, assert_1.default)(code);
console.log(code);
}
@ -80,9 +86,8 @@ exports.default = OakComponent({
});
this.navigateTo({
url: '/mobile/login',
onlyCaptcha: true,
eventLoggedIn: eventLoggedIn,
});
}
},
},
});

View File

@ -0,0 +1,25 @@
.container {
height: 100vh;
display: flex;
flex-direction: column;
}
.list {
:global {
.t-list-item__meta {
&-avatar {
width: auto !important;
height: auto !important;
background: transparent;
border-radius: unset;
}
&-title {
margin: 0 !important;
}
}
}
}

View File

@ -4,5 +4,5 @@ export default function render(props: WebComponentProps<EntityDict, 'mobile', tr
mobiles?: EntityDict['mobile']['OpSchema'][];
allowRemove: boolean;
}, {
goAddMobile: () => Promise<void>;
goAddMobile: () => void;
}>): JSX.Element;

View File

@ -4,12 +4,12 @@ var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var antd_mobile_1 = require("antd-mobile");
var icons_1 = require("@ant-design/icons");
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
var mobile_module_less_1 = tslib_1.__importDefault(require("./mobile.module.less"));
function render(props) {
var _this = this;
var _a = props.data, mobiles = _a.mobiles, allowRemove = _a.allowRemove;
var _b = props.methods, goAddMobile = _b.goAddMobile, removeItem = _b.removeItem, execute = _b.execute;
return ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default.container }, { children: [(0, jsx_runtime_1.jsx)(antd_mobile_1.List, tslib_1.__assign({ className: web_module_less_1.default.list }, { children: mobiles === null || mobiles === void 0 ? void 0 : mobiles.map(function (ele, index) { return ((0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ prefix: (0, jsx_runtime_1.jsx)(icons_1.MobileOutlined, {}), extra: allowRemove && (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ onClick: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
return ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: mobile_module_less_1.default.container }, { children: [(0, jsx_runtime_1.jsx)(antd_mobile_1.List, tslib_1.__assign({ className: mobile_module_less_1.default.list }, { children: mobiles === null || mobiles === void 0 ? void 0 : mobiles.map(function (ele, index) { return ((0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ prefix: (0, jsx_runtime_1.jsx)(icons_1.MobileOutlined, {}), extra: allowRemove && ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ onClick: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var result;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
@ -27,6 +27,6 @@ function render(props) {
case 3: return [2 /*return*/];
}
});
}); } }, { children: (0, jsx_runtime_1.jsx)(icons_1.DeleteOutlined, {}) })) }, { children: ele.mobile }), index)); }) })), (0, jsx_runtime_1.jsx)("div", { style: { flex: 1 } }), (0, jsx_runtime_1.jsx)(antd_mobile_1.Button, tslib_1.__assign({ block: true, size: "large", color: "primary", onClick: function () { return goAddMobile(); } }, { children: "\u6DFB\u52A0" }))] })));
}); } }, { children: (0, jsx_runtime_1.jsx)(icons_1.DeleteOutlined, {}) }))) }, { children: ele.mobile }), index)); }) })), (0, jsx_runtime_1.jsx)("div", { style: { flex: 1 } }), (0, jsx_runtime_1.jsx)(antd_mobile_1.Button, tslib_1.__assign({ block: true, size: "large", color: "primary", onClick: function () { return goAddMobile(); } }, { children: "\u7ED1\u5B9A" }))] })));
}
exports.default = render;

View File

@ -1,25 +1,12 @@
.container {
height: 100vh;
display: flex;
flex-direction: column;
background: var(--oak-bg-color-container);
box-shadow: 0 2px 3px #0000001a;
border-radius: 3px;
padding: 30px 32px;
}
.list {
:global {
.t-list-item__meta {
&-avatar {
width: auto !important;
height: auto !important;
background: transparent;
border-radius: unset;
}
&-title {
margin: 0 !important;
}
}
}
margin-top: 10px !important;
}

9
lib/pages/mobile/me/web.pc.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../general-app-domain';
export default function render(props: WebComponentProps<EntityDict, 'mobile', true, {
mobiles?: EntityDict['mobile']['OpSchema'][];
allowRemove: boolean;
showBack: boolean;
}, {
goAddMobile: () => void;
}>): JSX.Element;

View File

@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
var antd_1 = require("antd");
var icons_1 = require("@ant-design/icons");
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
var pageHeader_1 = tslib_1.__importDefault(require("../../../components/common/pageHeader"));
var login_1 = tslib_1.__importDefault(require("../../../pages/mobile/login"));
function render(props) {
var _this = this;
var _a = props.data, mobiles = _a.mobiles, allowRemove = _a.allowRemove, _b = _a.showBack, showBack = _b === void 0 ? false : _b;
var _c = props.methods, goAddMobile = _c.goAddMobile, removeItem = _c.removeItem, execute = _c.execute, sub = _c.sub;
var _d = tslib_1.__read((0, react_1.useState)(false), 2), open = _d[0], setOpen = _d[1];
var eventLoggedIn = "user:info:login:".concat(Date.now());
return ((0, jsx_runtime_1.jsxs)(pageHeader_1.default, tslib_1.__assign({ showBack: showBack, title: "\u6211\u7684\u624B\u673A\u53F7" }, { children: [(0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: web_module_less_1.default.container }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ type: "primary", onClick: function () {
setOpen(true);
} }, { children: "\u7ED1\u5B9A" })), (0, jsx_runtime_1.jsx)(antd_1.List, tslib_1.__assign({ bordered: true, className: web_module_less_1.default.list }, { children: mobiles === null || mobiles === void 0 ? void 0 : mobiles.map(function (ele, index) { return ((0, jsx_runtime_1.jsx)(antd_1.List.Item, tslib_1.__assign({ extra: allowRemove && ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ onClick: function () {
var modal = antd_1.Modal.confirm({
title: "\u786E\u8BA4\u5220\u9664\u5417\uFF1F\u5220\u9664\u540E\u65E0\u6CD5\u7528\u6B64\u53F7\u7801\u767B\u5F55",
okText: '确定',
cancelText: '取消',
onOk: function (e) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
removeItem(ele.id);
return [4 /*yield*/, execute()];
case 1:
_a.sent();
modal.destroy();
return [2 /*return*/];
}
});
}); },
onCancel: function (e) {
modal.destroy();
},
});
} }, { children: (0, jsx_runtime_1.jsx)(icons_1.DeleteOutlined, {}) }))) }, { children: (0, jsx_runtime_1.jsx)(antd_1.List.Item.Meta, { avatar: (0, jsx_runtime_1.jsx)(icons_1.MobileOutlined, {}), title: ele.mobile }) }), index)); }) }))] })), (0, jsx_runtime_1.jsx)(antd_1.Modal, tslib_1.__assign({ title: "\u7ED1\u5B9A\u624B\u673A\u53F7", open: open, destroyOnClose: true, footer: null, onCancel: function () {
setOpen(false);
} }, { children: (0, jsx_runtime_1.jsx)(login_1.default, { callback: function () {
setOpen(false);
}, oakPath: "$mobile/me-mobile/login", oakAutoUnmount: true }) }))] })));
}
exports.default = render;

View File

@ -46,11 +46,22 @@ exports.default = OakComponent({
isList: false,
formData: function (_a) {
var user = _a.data, features = _a.features;
var application = features.application.getApplication();
var avatar = (user === null || user === void 0 ? void 0 : user.extraFile$entity) && (user === null || user === void 0 ? void 0 : user.extraFile$entity[0]);
var avatarUrl = features.extraFile.getUrl(avatar);
var mobile = (((user === null || user === void 0 ? void 0 : user.mobile$user) && (user === null || user === void 0 ? void 0 : user.mobile$user[0])) || {}).mobile;
var genderOption = (user === null || user === void 0 ? void 0 : user.gender) &&
this.state.genderOptions.find(function (ele) { return ele.value === (user === null || user === void 0 ? void 0 : user.gender); });
var appType = application === null || application === void 0 ? void 0 : application.type;
var config = application === null || application === void 0 ? void 0 : application.config;
var appId;
var isSupportSyncWeChat = false; //是否支持微信公众号授权登录
if (appType === 'wechatPublic') {
var config2 = config;
var isService = config2 === null || config2 === void 0 ? void 0 : config2.isService; //是否服务号 服务号才能授权登录
appId = config2 === null || config2 === void 0 ? void 0 : config2.appId;
isSupportSyncWeChat = !!(isService && appId);
}
return {
id: user === null || user === void 0 ? void 0 : user.id,
name: user === null || user === void 0 ? void 0 : user.name,
@ -65,6 +76,7 @@ exports.default = OakComponent({
mobile: mobile,
userState: user === null || user === void 0 ? void 0 : user.userState,
idState: user === null || user === void 0 ? void 0 : user.idState,
isSupportSyncWeChat: isSupportSyncWeChat,
};
},
data: {
@ -96,6 +108,7 @@ exports.default = OakComponent({
birth: '出生日期',
},
birthEnd: '',
refreshing: false,
},
lifetimes: {
ready: function () {
@ -114,15 +127,41 @@ exports.default = OakComponent({
},
},
methods: {
setAvatar: function () {
this.setMessage({
type: 'warning',
content: '功能开发中',
refreshWechatPublicUserInfo: function () {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var err_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
this.setState({
refreshing: true,
});
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.features.token.refreshWechatPublicUserInfo()];
case 2:
_a.sent();
this.setState({
refreshing: false,
});
return [3 /*break*/, 4];
case 3:
err_1 = _a.sent();
this.setState({
refreshing: false,
});
throw err_1;
case 4: return [2 /*return*/];
}
});
});
},
setMobile: function () {
goAddMobile: function () {
this.navigateTo({
url: '/mobile/me',
}, {
showBack: true,
});
},
setVisibleMp: function (e) {

View File

@ -1,5 +1,5 @@
{
"navigationBarTitleText": "个人信息",
"navigationBarTitleText": "修改个人信息",
"oakDisablePulldownRefresh": true,
"usingComponents": {
"l-button": "../../../miniprogram_npm/lin-ui/button/index",

View File

@ -17,7 +17,7 @@
<l-list title="生日" data-attr="birth" bind:lintap="setVisibleMp">
<view slot="right-section" class="value">{{birthText || '未设置'}}</view>
</l-list>
<l-list title="手机号" bind:lintap="setMobile">
<l-list title="手机号" bind:lintap="goAddMobile">
<view slot="right-section" class="value">{{mobile || '未绑定'}}</view>
</l-list>
<!-- <l-list tag-position="right" is-link="{{false}}" title="用户状态">

View File

@ -0,0 +1,7 @@
{
"avatar": "头像",
"mobile": "手机号",
"manage": "管理",
"bind": "绑定",
"syncWeChat": "同步微信信息"
}

View File

@ -6,6 +6,14 @@
height: 100vh;
}
.syncWeChat {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
margin-top: 10px;
}
.avatar_container {
height: 220px;
display: flex;

View File

@ -14,13 +14,18 @@ declare type DataProps = {
value: string;
}>;
attrs: Record<string, string>;
id: string;
refreshing: boolean;
isSupportSyncWeChat: boolean;
appId: string;
};
declare type MethodsProps = {
setMobile: () => void;
goAddMobile: () => void;
setAvatar: () => void;
setVisible: (visible: boolean, attr: string) => void;
setCustomData: (attr: string, value: string | number) => void;
onConfirm: (attr: string) => Promise<void>;
refreshWechatPublicUserInfo: () => void;
};
export default function render(props: WebComponentProps<EntityDict, 'user', false, DataProps, MethodsProps>): JSX.Element;
export {};

View File

@ -4,20 +4,25 @@ var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var antd_mobile_1 = require("antd-mobile");
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
var avatar_1 = tslib_1.__importDefault(require("../../../components/extraFile/avatar"));
var mobile_module_less_1 = tslib_1.__importDefault(require("./mobile.module.less"));
function render(props) {
var data = props.data, methods = props.methods;
var t = methods.t, clean = methods.clean, setAvatar = methods.setAvatar, setVisible = methods.setVisible, setMobile = methods.setMobile;
var visible = data.visible, nickname = data.nickname, name = data.name, birth = data.birth, gender = data.gender, mobile = data.mobile, avatarUrl = data.avatarUrl, attr = data.attr;
return ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: mobile_module_less_1.default.container }, { children: [(0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: mobile_module_less_1.default.avatar_container }, { children: [(0, jsx_runtime_1.jsx)(antd_mobile_1.Avatar, { src: avatarUrl, className: mobile_module_less_1.default.avatar }), (0, jsx_runtime_1.jsx)(antd_mobile_1.Button, tslib_1.__assign({ size: 'mini', color: 'primary', onClick: setAvatar }, { children: "\u66F4\u65B0" }))] })), (0, jsx_runtime_1.jsxs)(antd_mobile_1.List, tslib_1.__assign({ className: mobile_module_less_1.default.list }, { children: [(0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: nickname ? nickname : '未设置', onClick: function () {
var t = methods.t, clean = methods.clean, setAvatar = methods.setAvatar, setVisible = methods.setVisible, goAddMobile = methods.goAddMobile, refreshWechatPublicUserInfo = methods.refreshWechatPublicUserInfo;
var oakFullpath = data.oakFullpath, visible = data.visible, nickname = data.nickname, name = data.name, birth = data.birth, gender = data.gender, mobile = data.mobile, avatarUrl = data.avatarUrl, attr = data.attr, id = data.id, isSupportSyncWeChat = data.isSupportSyncWeChat, refreshing = data.refreshing;
return ((0, jsx_runtime_1.jsxs)("div", tslib_1.__assign({ className: mobile_module_less_1.default.container }, { children: [isSupportSyncWeChat && ((0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: mobile_module_less_1.default.syncWeChat }, { children: (0, jsx_runtime_1.jsx)(antd_mobile_1.Button, tslib_1.__assign({ disabled: refreshing, color: "primary", fill: "outline", shape: "rounded", size: "small", onClick: function () {
refreshWechatPublicUserInfo();
} }, { children: t('syncWeChat') })) }))), (0, jsx_runtime_1.jsxs)(antd_mobile_1.List, tslib_1.__assign({ className: mobile_module_less_1.default.list }, { children: [(0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: (0, jsx_runtime_1.jsx)(avatar_1.default, { oakAutoUnmount: true, oakPath: oakFullpath
? oakFullpath + '.extraFile$entity'
: undefined, entity: "user", entityId: id }) }, { children: "\u5934\u50CF" })), (0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: nickname ? nickname : '未设置', onClick: function () {
setVisible(true, 'nickname');
} }, { children: t('user:attr.nickname') })), (0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: gender ? t("user:v.gender.".concat(gender)) : '未设置', onClick: function () {
setVisible(true, 'gender');
} }, { children: t('user:attr.gender') })), (0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: birth ? (0, dayjs_1.default)(birth).format('YYYY-MM-DD') : '未设置', onClick: function () {
setVisible(true, 'birth');
} }, { children: t('user:attr.birth') })), (0, jsx_runtime_1.jsx)(antd_mobile_1.List.Item, tslib_1.__assign({ extra: mobile ? mobile : '未设置', onClick: function () {
setMobile();
} }, { children: "\u624B\u673A\u53F7" }))] })), (0, jsx_runtime_1.jsx)(antd_mobile_1.Popup, tslib_1.__assign({ visible: visible, onMaskClick: function () {
goAddMobile();
} }, { children: t('mobile') }))] })), (0, jsx_runtime_1.jsx)(antd_mobile_1.Popup, tslib_1.__assign({ visible: visible, onMaskClick: function () {
clean();
setVisible(false, attr);
}, bodyStyle: {

View File

@ -15,11 +15,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'user', fals
value: string;
}>;
}, {
setMobile: () => void;
setAvatar: () => void;
setVisible: (visible: boolean, attr: string) => void;
setCustomData: (attr: string, value: string | number) => void;
onConfirm: (attr: string) => Promise<void>;
updateData: (attr: string, value: string | number) => void;
updateMyInfo: () => void;
goAddMobile: () => void;
}>): JSX.Element;

View File

@ -2,50 +2,66 @@
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
var antd_1 = require("antd");
var dayjs_1 = tslib_1.__importDefault(require("dayjs"));
var pageHeader_1 = tslib_1.__importDefault(require("../../../components/common/pageHeader"));
var avatar_1 = tslib_1.__importDefault(require("../../../components/extraFile/avatar"));
var login_1 = tslib_1.__importDefault(require("../../../pages/mobile/login"));
var web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
function Render(props) {
var data = props.data, methods = props.methods;
var t = methods.t, clean = methods.clean, setAvatar = methods.setAvatar, setVisible = methods.setVisible, setMobile = methods.setMobile, setCustomData = methods.setCustomData, onConfirm = methods.onConfirm, updateData = methods.updateData, updateMyInfo = methods.updateMyInfo;
var nickname = data.nickname, name = data.name, birth = data.birth, gender = data.gender, mobile = data.mobile, avatarUrl = data.avatarUrl, showBack = data.showBack, oakExecuting = data.oakExecuting, genderOptions = data.genderOptions;
return ((0, jsx_runtime_1.jsx)(pageHeader_1.default, tslib_1.__assign({ title: "\u4E2A\u4EBA\u4FE1\u606F", showBack: showBack }, { children: (0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default.container }, { children: (0, jsx_runtime_1.jsxs)(antd_1.Form, tslib_1.__assign({ labelCol: { xs: { span: 4 }, md: { span: 6 } }, wrapperCol: { xs: { span: 16 }, md: { span: 12 } } }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.name'), name: "name", rules: [
{
required: true,
},
] }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { placeholder: "", onChange: function (e) {
return methods.update({
name: e.target.value,
});
}, value: name }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.nickname'), name: "nickname", rules: [
{
required: true,
},
] }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { placeholder: "", onChange: function (e) {
return methods.update({
nickname: e.target.value,
});
}, value: nickname }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "gender", label: t('user:attr.gender') }, { children: (0, jsx_runtime_1.jsx)(antd_1.Space, tslib_1.__assign({ direction: "vertical" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Radio.Group, { value: data.gender, options: genderOptions, onChange: function (_a) {
var value = _a.target.value;
methods.update({ gender: value });
} }) })) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.birth'), name: "birth" }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.DatePicker, { placeholder: "\u8BF7\u9009\u62E9", format: "YYYY-MM-DD", inputReadOnly: true, allowClear: false, mode: "date", value: birth ? (0, dayjs_1.default)(birth) : undefined, disabledDate: function (current) {
if ((0, dayjs_1.default)(current).valueOf() >
(0, dayjs_1.default)().valueOf()) {
return true;
}
return false;
}, onChange: function (value) {
if (value) {
methods.update({
birth: (0, dayjs_1.default)(value).valueOf(),
var t = methods.t, updateMyInfo = methods.updateMyInfo, goAddMobile = methods.goAddMobile;
var nickname = data.nickname, name = data.name, birth = data.birth, gender = data.gender, mobile = data.mobile, avatarUrl = data.avatarUrl, showBack = data.showBack, oakExecuting = data.oakExecuting, genderOptions = data.genderOptions, oakFullpath = data.oakFullpath, oakDirty = data.oakDirty;
var _a = tslib_1.__read((0, react_1.useState)(false), 2), open = _a[0], setOpen = _a[1];
return ((0, jsx_runtime_1.jsxs)(pageHeader_1.default, tslib_1.__assign({ title: "\u4FEE\u6539\u4E2A\u4EBA\u4FE1\u606F", showBack: showBack }, { children: [(0, jsx_runtime_1.jsx)("div", tslib_1.__assign({ className: web_module_less_1.default.container }, { children: (0, jsx_runtime_1.jsxs)(antd_1.Form, tslib_1.__assign({ labelCol: { xs: { span: 4 }, md: { span: 6 } }, wrapperCol: { xs: { span: 16 }, md: { span: 12 } } }, { children: [(0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('avatar'), name: "extraFile$entity" }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(avatar_1.default, { oakAutoUnmount: true, oakPath: oakFullpath
? oakFullpath + '.extraFile$entity'
: undefined, entity: "user" }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.name'), name: "name", rules: [
{
required: true,
},
] }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { placeholder: "", onChange: function (e) {
return methods.update({
name: e.target.value,
});
}
} }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ wrapperCol: {
xs: { offset: 4 },
md: { offset: 6 },
} }, { children: (0, jsx_runtime_1.jsx)(antd_1.Space, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ disabled: oakExecuting, type: "primary", onClick: function () {
updateMyInfo();
} }, { children: "\u786E\u5B9A" })) }) }))] })) })) })));
}, value: name }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.nickname'), name: "nickname", rules: [
{
required: true,
},
] }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.Input, { placeholder: "", onChange: function (e) {
return methods.update({
nickname: e.target.value,
});
}, value: nickname }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ name: "gender", label: t('user:attr.gender') }, { children: (0, jsx_runtime_1.jsx)(antd_1.Space, tslib_1.__assign({ direction: "vertical" }, { children: (0, jsx_runtime_1.jsx)(antd_1.Radio.Group, { value: data.gender, options: genderOptions, onChange: function (_a) {
var value = _a.target.value;
methods.update({ gender: value });
} }) })) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('user:attr.birth'), name: "birth" }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsx)(antd_1.DatePicker, { placeholder: "\u8BF7\u9009\u62E9", format: "YYYY-MM-DD", inputReadOnly: true, allowClear: false, mode: "date", value: birth ? (0, dayjs_1.default)(birth) : undefined, disabledDate: function (current) {
if ((0, dayjs_1.default)(current).valueOf() >
(0, dayjs_1.default)().valueOf()) {
return true;
}
return false;
}, onChange: function (value) {
if (value) {
methods.update({
birth: (0, dayjs_1.default)(value).valueOf(),
});
}
} }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ label: t('mobile') }, { children: (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, jsx_runtime_1.jsxs)(antd_1.Space, { children: [(0, jsx_runtime_1.jsx)(antd_1.Typography, { children: mobile }), (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ size: "small", onClick: function () {
if (mobile) {
goAddMobile();
return;
}
setOpen(true);
} }, { children: mobile ? t('manage') : t('bind') }))] }) }) })), (0, jsx_runtime_1.jsx)(antd_1.Form.Item, tslib_1.__assign({ wrapperCol: {
xs: { offset: 4 },
md: { offset: 6 },
} }, { children: (0, jsx_runtime_1.jsx)(antd_1.Space, { children: (0, jsx_runtime_1.jsx)(antd_1.Button, tslib_1.__assign({ disabled: oakExecuting || !oakDirty, type: "primary", onClick: function () {
updateMyInfo();
} }, { children: "\u786E\u5B9A" })) }) }))] })) })), (0, jsx_runtime_1.jsx)(antd_1.Modal, tslib_1.__assign({ title: "\u7ED1\u5B9A\u624B\u673A\u53F7", open: open, destroyOnClose: true, footer: null, onCancel: function () {
setOpen(false);
} }, { children: (0, jsx_runtime_1.jsx)(login_1.default, { callback: function () {
setOpen(false);
}, oakPath: "$user/info-mobile/login", oakAutoUnmount: true }) }))] })));
}
exports.default = Render;

View File

@ -30,9 +30,6 @@ export declare class OakUserInfoUncompletedException<ED extends EntityDict & Bas
export declare class OakUserDisabledException<ED extends EntityDict & BaseEntityDict> extends OakUserException<ED> {
constructor(message?: string);
}
export declare class OakWechatPublicNeedReloginException<ED extends EntityDict & BaseEntityDict> extends OakUserException<ED> {
constructor(message?: string);
}
export declare class OakTokenExpiredException<ED extends EntityDict & BaseEntityDict> extends OakUserException<ED> {
constructor(message?: string);
}

View File

@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeException = exports.OakTokenExpiredException = exports.OakWechatPublicNeedReloginException = exports.OakUserDisabledException = exports.OakUserInfoUncompletedException = exports.OakMobileUnsetException = exports.OakChangeLoginWayException = exports.OakDistinguishUserException = exports.OakNotEnoughMoneyException = void 0;
exports.makeException = exports.OakTokenExpiredException = exports.OakUserDisabledException = exports.OakUserInfoUncompletedException = exports.OakMobileUnsetException = exports.OakChangeLoginWayException = exports.OakDistinguishUserException = exports.OakNotEnoughMoneyException = void 0;
var tslib_1 = require("tslib");
var types_1 = require("oak-domain/lib/types");
var OakNotEnoughMoneyException = /** @class */ (function (_super) {
@ -85,14 +85,6 @@ var OakUserDisabledException = /** @class */ (function (_super) {
return OakUserDisabledException;
}(types_1.OakUserException));
exports.OakUserDisabledException = OakUserDisabledException;
var OakWechatPublicNeedReloginException = /** @class */ (function (_super) {
tslib_1.__extends(OakWechatPublicNeedReloginException, _super);
function OakWechatPublicNeedReloginException(message) {
return _super.call(this, message || '您的授权过期,需要重新登录') || this;
}
return OakWechatPublicNeedReloginException;
}(types_1.OakUserException));
exports.OakWechatPublicNeedReloginException = OakWechatPublicNeedReloginException;
var OakTokenExpiredException = /** @class */ (function (_super) {
tslib_1.__extends(OakTokenExpiredException, _super);
function OakTokenExpiredException(message) {
@ -138,11 +130,6 @@ function makeException(data) {
e.setOpRecords(opRecords);
return e;
}
case 'OakWechatPublicNeedReloginException': {
var e = new OakWechatPublicNeedReloginException(message);
e.setOpRecords(opRecords);
return e;
}
default: {
return;
}

View File

@ -8,9 +8,9 @@ import { CreateOperationData as CreateWechatUser } from '../general-app-domain/W
import { CreateOperationData as CreateUser, Schema as User } from '../general-app-domain/User/Schema';
import { Operation as ExtraFileOperation } from '../general-app-domain/ExtraFile/Schema';
import { isEqual } from 'oak-domain/lib/utils/lodash';
import { OakRowInconsistencyException, OakUserException, OakUserUnpermittedException } from 'oak-domain/lib/types';
import { OakRowInconsistencyException, OakUnloggedInException, OakUserException, OakUserUnpermittedException } from 'oak-domain/lib/types';
import { composeFileUrl, decomposeFileUrl } from '../utils/extraFile';
import { OakChangeLoginWayException, OakDistinguishUserException, OakUserDisabledException, OakWechatPublicNeedReloginException } from '../types/Exception';
import { OakChangeLoginWayException, OakDistinguishUserException, OakUserDisabledException } from '../types/Exception';
import { encryptPasswordSha1 } from '../utils/password';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { tokenProjection } from '../types/projection';
@ -369,6 +369,70 @@ export async function loginByMobile<ED extends EntityDict, Cxt extends BackendRu
return tokenId;
}
async function setUserInfoFromWechat<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(user: Partial<ED['user']['Schema']>, userInfo: {
nickname?: string; avatar?: string; gender?: 'male' | 'female';
}, context: Cxt) {
const application = context.getApplication();
const config = application?.system?.config || application?.system?.platform?.config;
const { nickname, gender, avatar } = userInfo;
const { nickname: originalNickname, gender: originalGender, extraFile$entity } = user;
const updateData = {};
if (nickname && nickname !== originalNickname) {
Object.assign(updateData, {
nickname,
});
}
if (gender && gender !== originalGender) {
Object.assign(updateData, {
gender,
});
}
if (avatar && (extraFile$entity?.length === 0 ||
composeFileUrl(extraFile$entity![0], config) !== avatar)) {
// 需要更新新的avatar extra file
const extraFileOperations: ExtraFileOperation['data'][] = [
{
id: await generateNewIdAsync(),
action: 'create',
data: Object.assign(
{
id: await generateNewIdAsync(),
tag1: 'avatar',
entity: 'user',
entityId: user.id,
objectId: await generateNewIdAsync(),
},
decomposeFileUrl(avatar)
),
},
];
if (extraFile$entity!.length > 0) {
extraFileOperations.push({
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: extraFile$entity![0].id,
},
});
}
Object.assign(updateData, {
extraFile$entity: extraFileOperations,
});
}
if (Object.keys(updateData).length > 0) {
await context.operate('user', {
id: await generateNewIdAsync(),
action: 'update',
data: updateData,
filter: {
id: user.id!,
}
}, {});
}
}
async function tryRefreshWechatPublicUserInfo<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>(wechatUserId: string, context: Cxt) {
const [wechatUser] = await context.select('wechatUser', {
data: {
@ -378,19 +442,70 @@ async function tryRefreshWechatPublicUserInfo<ED extends EntityDict, Cxt extends
atExpiredAt: 1,
rtExpiredAt: 1,
scope: 1,
openId: 1,
user: {
id: 1,
nickname: 1,
gender: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
id: 1,
tag1: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
entity: 1,
entityId: 1,
},
filter: {
tag1: 'avatar',
},
},
},
},
filter: {
id: wechatUserId,
}
}, { dontCollect: true });
const { accessToken, refreshToken, atExpiredAt, rtExpiredAt, scope } = wechatUser;
const application = context.getApplication();
const { type, config } = application!;
assert(type === 'wechatPublic' && config!.type === 'wechatPublic');
let appId: string, appSecret: string;
const config2 = config as WechatPublicConfig;
appId = config2.appId;
appSecret = config2.appSecret;
const wechatInstance = WechatSDK.getInstance(appId!, appSecret!, type!) as WechatPublicInstance;
let { accessToken, refreshToken, atExpiredAt, rtExpiredAt, scope, openId, user } = wechatUser;
const now = Date.now();
assert(scope!.toLowerCase().includes('userinfo'));
if (rtExpiredAt! < now) {
// refreshToken过期直接返回失败
throw new OakWechatPublicNeedReloginException();
// refreshToken过期直接返回未登录异常,使用户去重新登录
throw new OakUnloggedInException();
}
if (atExpiredAt! < now) {
// 刷新accessToken
const { accessToken: at2, atExpiredAt: ate2, scope: s2 } = await wechatInstance.refreshUserAccessToken(refreshToken!);
await context.operate('wechatUser', {
id: await generateNewIdAsync(),
action: 'update',
data: {
accessToken: at2,
atExpiredAt: ate2,
scope: s2,
}
}, { dontCollect: true, dontCreateModi: true, dontCreateOper: true })
accessToken = at2;
}
const { nickname, gender, avatar } = await wechatInstance.getUserInfo(accessToken!, openId!);
await setUserInfoFromWechat<ED, Cxt>(user!, {nickname, gender: gender as 'male', avatar}, context);
}
export async function refreshWechatPublicUserInfo<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>>({}, context: Cxt) {
@ -1000,6 +1115,7 @@ export async function syncUserInfoWechatMp<ED extends EntityDict, Cxt extends Ba
user: {
id: 1,
nickname: 1,
gender: 1,
extraFile$entity: {
$entity: 'extraFile',
data: {
@ -1038,63 +1154,7 @@ export async function syncUserInfoWechatMp<ED extends EntityDict, Cxt extends Ba
// const result = wechatInstance.decryptData(sessionKey as string, encryptedData, iv, signature);
// 实测发现解密出来的和userInfo完全一致……
// console.log(result);
const { nickname: originNickname, extraFile$entity } = user as User;
const updateData = {};
if (nickname !== originNickname) {
Object.assign(updateData, {
nickname,
});
}
if (
extraFile$entity?.length === 0 ||
composeFileUrl(extraFile$entity![0], config) !== avatarUrl
) {
// 需要更新新的avatar extra file
const extraFileOperations: ExtraFileOperation['data'][] = [
{
id: await generateNewIdAsync(),
action: 'create',
data: Object.assign(
{
id: await generateNewIdAsync(),
tag1: 'avatar',
entity: 'user',
entityId: userId,
objectId: await generateNewIdAsync(),
},
decomposeFileUrl(avatarUrl)
),
},
];
if (extraFile$entity!.length > 0) {
extraFileOperations.push({
id: await generateNewIdAsync(),
action: 'remove',
data: {},
filter: {
id: extraFile$entity![0].id,
},
});
}
Object.assign(updateData, {
extraFile$entity: extraFileOperations,
});
}
if (Object.keys(updateData).length > 0) {
await context.operate('user', {
id: await generateNewIdAsync(),
action: 'update',
data: updateData,
filter: {
id: userId!,
}
}, {
dontCollect: true,
});
}
// todo update nickname/avatar in wechatUser
await setUserInfoFromWechat<ED, Cxt>(user!, { nickname, avatar: avatarUrl }, context);
}

View File

@ -13,7 +13,6 @@ export interface Schema extends EntityShape {
passwordSha1?: Text;
birth?: Datetime;
gender?: 'male' | 'female';
avatar?: Image;
idCardType?: 'ID-Card' | 'passport' | 'Mainland-passport';
idNumber?: String<32>;
ref?: Schema;
@ -98,7 +97,6 @@ const locale: LocaleDef<Schema, Action, '', {
password: '密码',
passwordSha1: 'sha1加密密码',
gender: '性别',
avatar: '头像',
idCardType: '证件类型',
idNumber: '证件号码',
ref: '指向用户',

View File

@ -32,7 +32,7 @@ type DataProps = {
genderOptions: Array<{ label: string; value: string }>;
attrs: Record<string, string>;
id: string;
refreshing: string;
refreshing: boolean;
isSupportSyncWeChat: boolean;
appId: string;
};

View File

@ -80,12 +80,6 @@ export class OakUserDisabledException<ED extends EntityDict & BaseEntityDict> ex
}
}
export class OakWechatPublicNeedReloginException<ED extends EntityDict & BaseEntityDict> extends OakUserException<ED> {
constructor(message?: string) {
super(message || '您的授权过期,需要重新登录');
}
}
export class OakTokenExpiredException<ED extends EntityDict & BaseEntityDict> extends OakUserException<ED> {
constructor(message?: string) {
@ -137,11 +131,6 @@ export function makeException<ED extends EntityDict & BaseEntityDict>(data: {
e.setOpRecords(opRecords);
return e;
}
case 'OakWechatPublicNeedReloginException': {
const e = new OakWechatPublicNeedReloginException(message);
e.setOpRecords(opRecords);
return e;
}
default: {
return;
}