oak-general-business/src/components/applicationPassport/index.ts

405 lines
16 KiB
TypeScript

import { groupBy, isEqual, uniq } from "oak-domain/lib/utils/lodash";
import { EntityDict } from "../../oak-app-domain";
import { EmailConfig, PfwConfig, SmsConfig, MfwConfig } from "../../entities/Passport";
import { WebConfig } from "../../entities/Application";
import { assert } from "oak-domain/lib/utils/assert";
import { generateNewId, generateNewIdAsync } from "oak-domain/lib/utils/uuid";
import { ReactComponentProps } from "oak-frontend-base";
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
type RowItem = {
aId: string;
aName: string;
typeRecords: TypeRecord;
defaultOptions: {
label: string; //passport.type
value: string; //applicationPassport.id
}[];
defaultValue: string; //applicationPassport.id
};
type TypeRecord = Record<string, {
render: 'switch' | 'select';
passportOptions?: PassportOption[];
checkedValue?: string;
apId?: string;
pId?: string;
checked?: boolean;
disabled: boolean;
disabledTip: string;
}>;
type PassportOption = {
label: string;
value: string;
apId: string;
disabled: boolean;
disabledTip?: string;
}
export default OakComponent({
entity: 'applicationPassport',
isList: true,
projection: {
id: 1,
applicationId: 1,
application: {
id: 1,
name: 1,
type: 1,
systemId: 1,
},
passportId: 1,
passport: {
id: 1,
type: 1,
systemId: 1,
enabled: 1,
},
isDefault: 1,
},
properties: {
systemId: '',
},
filters: [
{
filter() {
const { systemId } = this.props;
return {
application: {
systemId,
},
passport: {
systemId,
},
}
}
}
],
formData({ data }) {
const aps = data.filter((ele) => ele.$$deleteAt$$ !== 1);
return {
aps,
};
},
listeners: {
async 'aps,applications,passports'(prev, next) {
if (!this.arraysAreEqual(prev.aps, next.aps) || !this.arraysAreEqual(prev.applications, next.applications) || !this.arraysAreEqual(prev.passports, next.passports)) {
let apArray: RowItem[] = [];
const records = groupBy(next.passports, 'type');
if (next.applications && next.applications.length > 0 && next.passports && next.passports.length > 0) {
for (const a of next.applications) {
let item = {
aId: a.id,
aName: a.name,
typeRecords: {},
defaultOptions: [],
defaultValue: '',
};
let typeRecords: TypeRecord = {};
for (const key of Object.keys(records)) {
const r = records[key];
const render = this.getRender(key, r, a.type);
if (render === 'select') {
const passportOptions = r.map((ele) => {
const { disabled, disabledTip } = this.checkDisabled(a, ele);
return {
label: ele.type === 'email' ? ele.config.account : ele.config.appId,
value: ele.id,
apId: generateNewId(),
disabled,
disabledTip,
}
});
const d = !passportOptions.find((ele) => !ele.disabled);
let disabledTip = '';
if (d) {
disabledTip = '暂不支持该登录方式';
}
Object.assign(typeRecords, { [key]: { render, passportOptions, chekedValue: undefined, disabled: d, disabledTip } });
}
else {
const { disabled, disabledTip } = this.checkDisabled(a, r[0]);
const apId = await generateNewIdAsync();
Object.assign(typeRecords, { [key]: { render, pId: r[0].id, checked: false, disabled, disabledTip, apId, } });
}
}
Object.assign(item, { typeRecords });
apArray.push(item);
}
if (next.aps && next.aps.length > 0) {
for (const ap of next.aps) {
const aIdx = apArray.findIndex((ele) => ele.aId === ap.applicationId);
if (aIdx !== -1) {
const p = ap.passport;
const t = p.type;
if (apArray[aIdx].typeRecords[t].render === 'select') {
apArray[aIdx].typeRecords[t].checkedValue = p.id;
apArray[aIdx].typeRecords[t].apId = ap.id;
const option = apArray[aIdx].typeRecords[t].passportOptions?.find((ele) => ele.value === p.id);
option && Object.assign(option, { apId: ap.id });
} else {
if (apArray[aIdx].typeRecords[t].pId === p.id) {
apArray[aIdx].typeRecords[t].checked = true;
apArray[aIdx].typeRecords[t].apId = ap.id;
}
}
apArray[aIdx].defaultOptions.push({
label: this.t(`passport:v.type.${p.type}`),
value: ap.id,
});
if (ap.isDefault) {
apArray[aIdx].defaultValue = ap.id;
}
}
}
}
}
this.setState({
apArray,
})
}
}
},
data: {
applications: [] as EntityDict['application']['Schema'][],
passports: [] as EntityDict['passport']['Schema'][],
apArray: [] as RowItem[],
types: [] as EntityDict['passport']['Schema']['type'][],
},
lifetimes: {
async ready() {
const { systemId } = this.props;
const { data: applicationDatas } = await this.features.cache.refresh('application',
{
data: {
id: 1,
name: 1,
type: 1,
config: 1,
systemId: 1,
},
filter: {
systemId,
}
}
);
const { data: passportDatas } = await this.features.cache.refresh('passport',
{
data: {
id: 1,
type: 1,
config: 1,
enabled: 1,
systemId: 1,
},
filter: {
systemId,
enabled: true,
},
sorter: [{
$attr: {
$$updateAt$$: 1,
},
$direction: 'desc'
}]
}
);
const applications = applicationDatas as EntityDict['application']['Schema'][];
const passports = passportDatas as EntityDict['passport']['Schema'][];
const types = uniq(passports.map((ele) => ele.type));
this.setState({
applications,
passports,
types,
})
}
},
methods: {
arraysAreEqual(first: Object[], second: Object[]) {
if (first?.length !== second?.length) {
return false;
}
for (let i = 0; i < first?.length; ++i) {
if (!isEqual(first[i], second[i])) {
return false;
}
}
return true;
},
checkDisabled(application: EntityDict['application']['Schema'], passport: EntityDict['passport']['Schema']) {
const { type: aType, config: aConfig } = application;
const { type: pType, config: pConfig } = passport;
switch (pType) {
case 'sms':
if (!(pConfig as SmsConfig).mockSend) {
if (!(pConfig as SmsConfig).templateName || (pConfig as SmsConfig).templateName === '') {
return {
disabled: true,
disabledTip: '短信登录未配置验证码模板名称',
}
}
if (!(pConfig as SmsConfig).defaultOrigin) {
return {
disabled: true,
disabledTip: '短信登录未配置默认渠道',
}
}
}
break;
case 'email':
if (!(pConfig as EmailConfig).mockSend) {
if (!(pConfig as EmailConfig).account || (pConfig as EmailConfig).account === '') {
return {
disabled: true,
disabledTip: '邮箱登录未配置账号',
}
}
else if (!(pConfig as EmailConfig).subject || (pConfig as EmailConfig).subject === '') {
return {
disabled: true,
disabledTip: '邮箱登录未配置邮件主题',
}
} else if (
(!(pConfig as EmailConfig).text || (pConfig as EmailConfig).text === '' || !(pConfig as EmailConfig).text?.includes('${code}')) &&
(!(pConfig as EmailConfig).html || (pConfig as EmailConfig).html === '' || !(pConfig as EmailConfig).html?.includes('${code}'))
) {
return {
disabled: true,
disabledTip: '邮箱登录未配置邮件内容模板',
}
}
}
break;
case 'wechatPublicForWeb':
if (!(pConfig as PfwConfig).appId || (pConfig as PfwConfig).appId === '') {
return {
disabled: true,
disabledTip: '公众号授权登录未配置appId',
}
}
break;
case 'wechatMpForWeb':
if (!(pConfig as MfwConfig).appId || (pConfig as MfwConfig).appId === '') {
return {
disabled: true,
disabledTip: '小程序授权登录未配置appId',
}
}
break;
default:
break;
}
switch (aType) {
case 'web':
if (pType === 'wechatWeb') {
//微信网站登录 application需配置微信网站appId
const { appId } = (aConfig as WebConfig).wechat || {};
if (!appId || appId === '') {
return {
disabled: true,
disabledTip: '当前application未配置微信网站appId',
}
}
} else if (pType === 'wechatMp' || pType === 'wechatPublic') {
return {
disabled: true,
disabledTip: '当前application不支持该登录方式',
}
}
break;
case 'wechatMp':
if (['wechatPublic', 'wechatWeb', 'wechatMpForWeb', 'wechatPublicForWeb'].includes(pType)) {
return {
disabled: true,
disabledTip: '当前application不支持该登录方式',
}
}
break;
case 'wechatPublic':
if (['wechatMp', 'wechatWeb', 'wechatMpForWeb', 'wechatPublicForWeb'].includes(pType)) {
return {
disabled: true,
disabledTip: '当前application不支持该登录方式',
}
}
break;
case 'native':
if (['wechatMp', 'wechatPublic', 'wechatWeb', 'wechatMpForWeb', 'wechatPublicForWeb'].includes(pType)) {
return {
disabled: true,
disabledTip: '当前application不支持该登录方式',
}
}
break;
default:
break;
}
return {
disabled: false,
disabledTip: undefined,
}
},
async onCheckedChange(aId: string, pId: string, checked: boolean, apId?: string) {
if (checked) {
//create applicationPassport
this.addItem({
applicationId: aId,
passportId: pId,
isDefault: true,
})
} else {
//remove id为apId的applicationPassport
apId && this.removeItem(apId);
}
},
checkLastOne(aId: string, pId: string) {
const { apArray } = this.state;
const idx = apArray.findIndex((ele) => ele.aId === aId);
if (idx !== -1) {
const records = apArray[idx].typeRecords;
for (const key of Object.keys(records)) {
const r = records[key];
if ((r.checkedValue && r.checkedValue !== pId && !!r.checkedValue) || (r.pId && r.pId !== pId && !!r.checked)) {
return false;
}
}
return true;
}
return false;
},
async onSelectChange(aId: string, addPId?: string, removeApId?: string) {
if (removeApId) {
removeApId && this.removeItem(removeApId);
}
if (addPId) {
this.addItem({
applicationId: aId,
passportId: addPId,
isDefault: true,
})
}
},
getRender(pType: EntityDict['passport']['Schema']['type'], passports: EntityDict['passport']['Schema'][], aType: EntityDict['application']['Schema']['type']) {
let render: 'switch' | 'select' = 'switch';
if (passports.length > 1) {
if (aType === 'web' || (['wechatMp', 'wechatPublic'].includes(aType) && ['email', 'sms'].includes(pType))) {
render = 'select';
}
}
return render;
}
}
}) as <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(
props: ReactComponentProps<
ED2,
T2,
true,
{
systemId: string;
}
>
) => React.ReactElement;