增加了pay的attrUpdateMatrix
This commit is contained in:
parent
a9161e7210
commit
7082a521cb
|
|
@ -0,0 +1 @@
|
||||||
|
<view>account detail</view>
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||||
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
|
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
|
||||||
|
accountOpers: RowWithActions<EntityDict, 'accountOper'>[];
|
||||||
|
}>): string;
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default function Render(props) {
|
||||||
|
const { accountOpers } = props.data;
|
||||||
|
const { t } = props.methods;
|
||||||
|
return '还没有实现,todo';
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,14 @@ export default OakComponent({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setUseAccount(v) {
|
setUseAccount(v) {
|
||||||
this.setState({ useAccount: v }, () => this.tryCreatePay());
|
const { accountAvailMax } = this.props;
|
||||||
|
const { order } = this.state;
|
||||||
|
const accountMaxPrice = Math.min(accountAvailMax, order.price);
|
||||||
|
this.setState({
|
||||||
|
useAccount: v,
|
||||||
|
accountPrice: accountMaxPrice,
|
||||||
|
rest: order.price - accountMaxPrice,
|
||||||
|
}, () => this.tryCreatePay());
|
||||||
},
|
},
|
||||||
setAccountPrice(price) {
|
setAccountPrice(price) {
|
||||||
const { order } = this.state;
|
const { order } = this.state;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
/// <reference types="react" />
|
||||||
|
export default function Info(props: {
|
||||||
|
price: number;
|
||||||
|
t: (k: string) => string;
|
||||||
|
}): import("react").JSX.Element;
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Styles from './info.module.less';
|
||||||
|
export default function Info(props) {
|
||||||
|
const { price, t } = props;
|
||||||
|
return (<div className={Styles.info}>
|
||||||
|
<div className={Styles.should}>{t('price')}</div>
|
||||||
|
<div className={Styles.price}>
|
||||||
|
<div>{t('common::pay.symbol')}</div>
|
||||||
|
<div>{price}</div>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin: 8px;
|
||||||
|
height: 140px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: var(--oak-color-info);
|
||||||
|
|
||||||
|
.should {
|
||||||
|
font-size: large;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.price {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 3px;
|
||||||
|
font-size: xx-large;
|
||||||
|
font-weight: bolder;
|
||||||
|
color: var(--oak-color-primary);
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { WebComponentProps } from 'oak-frontend-base';
|
||||||
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
|
import { AccountPayConfig, PayConfig } from '../../../types/PayConfig';
|
||||||
|
export default function Render(props: WebComponentProps<EntityDict, 'order', false, {
|
||||||
|
accountId?: string;
|
||||||
|
accountAvailMax: number;
|
||||||
|
order: EntityDict['order']['OpSchema'];
|
||||||
|
activePay?: EntityDict['pay']['OpSchema'];
|
||||||
|
accountConfig?: AccountPayConfig;
|
||||||
|
payConfig?: PayConfig;
|
||||||
|
accountPrice: number;
|
||||||
|
channel?: string;
|
||||||
|
meta?: object;
|
||||||
|
useAccount: boolean;
|
||||||
|
rest: number;
|
||||||
|
legal: false;
|
||||||
|
}, {
|
||||||
|
setAccountPrice: (price: number) => void;
|
||||||
|
onPickChannel: (channel: string) => void;
|
||||||
|
onSetChannelMeta: (meta?: object) => void;
|
||||||
|
setUseAccount: (v: boolean) => void;
|
||||||
|
}>): React.JSX.Element | null;
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { ToYuan, ToCent } from 'oak-domain/lib/utils/money';
|
||||||
|
import Styles from './web.mobile.module.less';
|
||||||
|
import PayChannelPicker from '../../pay/channelPicker';
|
||||||
|
import { InputNumber } from 'antd';
|
||||||
|
import { Checkbox, Divider, ErrorBlock } from 'antd-mobile';
|
||||||
|
import Info from './info';
|
||||||
|
function RenderPayChannel(props) {
|
||||||
|
const { price, payConfig, t, channel, meta, onPick, onSetMeta } = props;
|
||||||
|
return (<div className={Styles.pc1}>
|
||||||
|
<div className={Styles.content}>
|
||||||
|
<div>
|
||||||
|
{t('choose', { price: ToYuan(price) })}
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<PayChannelPicker payConfig={payConfig} channel={channel} meta={meta} onPick={onPick} onSetMeta={onSetMeta}/>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
function RenderAccountPay(props) {
|
||||||
|
const { max, t, accountPrice, setAccountPrice, useAccount, setUseAccount, accountAvail } = props;
|
||||||
|
return (<div className={Styles.pc1}>
|
||||||
|
<div className={Styles.content}>
|
||||||
|
<Checkbox checked={useAccount} onChange={() => {
|
||||||
|
setUseAccount(!useAccount);
|
||||||
|
if (useAccount) {
|
||||||
|
setAccountPrice(0);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{t('useAccount')}
|
||||||
|
</Checkbox>
|
||||||
|
{useAccount && (<>
|
||||||
|
<Divider />
|
||||||
|
<div>
|
||||||
|
<InputNumber max={ToYuan(max)} addonAfter="¥" value={typeof accountPrice === 'number' ? ToYuan(accountPrice) : null} onChange={(v) => {
|
||||||
|
if (typeof v === 'number') {
|
||||||
|
setAccountPrice(Math.floor(ToCent(v)));
|
||||||
|
}
|
||||||
|
}}/>
|
||||||
|
<div className={Styles.tips}>{t('accountMax', { max: ToYuan(accountAvail) })}</div>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
export default function Render(props) {
|
||||||
|
const { accountId, accountAvailMax, legal, accountPrice, useAccount, order, activePay, payConfig, channel, meta, rest } = props.data;
|
||||||
|
const { t, setAccountPrice, onPickChannel, onSetChannelMeta, setUseAccount } = props.methods;
|
||||||
|
if (order) {
|
||||||
|
if (activePay) {
|
||||||
|
return (<ErrorBlock status="default" title={t('paying')}/>);
|
||||||
|
}
|
||||||
|
if (!legal) {
|
||||||
|
return (<ErrorBlock status="default" title={t('illegalState', { state: t(`order:v.iState.${order.iState}`) })}/>);
|
||||||
|
}
|
||||||
|
return (<div className={Styles.container}>
|
||||||
|
<Info t={t} price={ToYuan(order.price)}/>
|
||||||
|
{accountId && accountAvailMax && <div className={Styles.ctrl}>
|
||||||
|
<RenderAccountPay max={Math.min(accountAvailMax, rest + accountPrice)} t={t} setAccountPrice={setAccountPrice} useAccount={useAccount} setUseAccount={setUseAccount} accountPrice={accountPrice} accountAvail={accountAvailMax}/>
|
||||||
|
</div>}
|
||||||
|
{!!(rest && rest > 0) && <div className={Styles.ctrl}>
|
||||||
|
<RenderPayChannel payConfig={payConfig} price={rest} t={t} channel={channel} meta={meta} onPick={onPickChannel} onSetMeta={onSetChannelMeta}/>
|
||||||
|
</div>}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
background-color: var(--oak-bg-color-page);
|
||||||
|
// min-height: 500px;
|
||||||
|
|
||||||
|
.ctrl {
|
||||||
|
margin: 8px;
|
||||||
|
margin-top: 10px;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.pc1 {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background-color: var(--oak-bg-color-container);
|
||||||
|
padding: 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
.tips {
|
||||||
|
color: var(--oak-color-warning);
|
||||||
|
font-size: small;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { ToYuan, ToCent } from 'oak-domain/lib/utils/money';
|
||||||
import Styles from './web.pc.module.less';
|
import Styles from './web.pc.module.less';
|
||||||
import PayChannelPicker from '../../pay/channelPicker';
|
import PayChannelPicker from '../../pay/channelPicker';
|
||||||
import { Divider, Checkbox, InputNumber, Flex, Result } from 'antd';
|
import { Divider, Checkbox, InputNumber, Flex, Result } from 'antd';
|
||||||
|
import Info from './info';
|
||||||
function RenderPayChannel(props) {
|
function RenderPayChannel(props) {
|
||||||
const { price, payConfig, t, channel, meta, onPick, onSetMeta } = props;
|
const { price, payConfig, t, channel, meta, onPick, onSetMeta } = props;
|
||||||
return (<div className={Styles.pc1}>
|
return (<div className={Styles.pc1}>
|
||||||
|
|
@ -52,13 +53,7 @@ export default function Render(props) {
|
||||||
return (<Result status="warning" title={t('illegalState', { state: t(`order:v.iState.${order.iState}`) })}/>);
|
return (<Result status="warning" title={t('illegalState', { state: t(`order:v.iState.${order.iState}`) })}/>);
|
||||||
}
|
}
|
||||||
return (<div className={Styles.container}>
|
return (<div className={Styles.container}>
|
||||||
<div className={Styles.info}>
|
<Info t={t} price={ToYuan(order.price)}/>
|
||||||
<div className={Styles.should}>{t('price')}</div>
|
|
||||||
<div className={Styles.price}>
|
|
||||||
<div>{t('common::pay.symbol')}</div>
|
|
||||||
<div>{ToYuan(order.price)}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{accountId && accountAvailMax && <div className={Styles.ctrl}>
|
{accountId && accountAvailMax && <div className={Styles.ctrl}>
|
||||||
<RenderAccountPay max={Math.min(accountAvailMax, rest + accountPrice)} t={t} setAccountPrice={setAccountPrice} useAccount={useAccount} setUseAccount={setUseAccount} accountPrice={accountPrice} accountAvail={accountAvailMax}/>
|
<RenderAccountPay max={Math.min(accountAvailMax, rest + accountPrice)} t={t} setAccountPrice={setAccountPrice} useAccount={useAccount} setUseAccount={setUseAccount} accountPrice={accountPrice} accountAvail={accountAvailMax}/>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
|
||||||
|
|
@ -7,30 +7,6 @@
|
||||||
background-color: var(--oak-bg-color-page);
|
background-color: var(--oak-bg-color-page);
|
||||||
// min-height: 500px;
|
// min-height: 500px;
|
||||||
|
|
||||||
.info {
|
|
||||||
margin: 8px;
|
|
||||||
height: 140px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: var(--oak-color-info);
|
|
||||||
|
|
||||||
.should {
|
|
||||||
font-size: large;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.price {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
padding: 3px;
|
|
||||||
font-size: xx-large;
|
|
||||||
font-weight: bolder;
|
|
||||||
color: var(--oak-color-primary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ctrl {
|
.ctrl {
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export default function Render(props) {
|
||||||
}
|
}
|
||||||
}} value={channel}>
|
}} value={channel}>
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
{payConfig.map((v) => (<Radio value={v.channel} key={v.channel}>
|
{payConfig.map((v) => (<Radio className={Styles.radio} value={v.channel} key={v.channel}>
|
||||||
<Space direction="horizontal" align='center'>
|
<Space direction="horizontal" align='center'>
|
||||||
<span className={Styles.span}>
|
<span className={Styles.span}>
|
||||||
{t(`payChannel::${v.channel}`)}
|
{t(`payChannel::${v.channel}`)}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
.span {
|
.span {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.radio {
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
@ -38,6 +38,7 @@ export default OakComponent({
|
||||||
application,
|
application,
|
||||||
iStateColor,
|
iStateColor,
|
||||||
payConfig,
|
payConfig,
|
||||||
|
closable: !!(data?.["#oakLegalActions"]?.includes('close')),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
features: ['application'],
|
features: ['application'],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,19 @@
|
||||||
|
import React from 'react';
|
||||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
|
import { OfflinePayConfig, PayConfig } from '../../../types/PayConfig';
|
||||||
|
export declare function RenderOffline(props: {
|
||||||
|
pay: RowWithActions<EntityDict, 'pay'>;
|
||||||
|
t: (key: string) => string;
|
||||||
|
offline: OfflinePayConfig;
|
||||||
|
updateMeta: (meta: any) => void;
|
||||||
|
metaUpdatable: boolean;
|
||||||
|
}): React.JSX.Element;
|
||||||
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
|
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
|
||||||
pay: RowWithActions<EntityDict, 'pay'>;
|
pay: RowWithActions<EntityDict, 'pay'>;
|
||||||
}>): null;
|
application?: EntityDict['application']['Schema'];
|
||||||
|
iStateColor?: string;
|
||||||
|
payConfig?: PayConfig;
|
||||||
|
onClose: () => undefined;
|
||||||
|
closable: boolean;
|
||||||
|
}>): React.JSX.Element | null;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,156 @@
|
||||||
export default function Render(props) {
|
import React, { useEffect, useState } from 'react';
|
||||||
const { pay } = props.data;
|
import { Card, Tag, List, Button, Modal, Form, Selector, TextArea } from 'antd-mobile';
|
||||||
const { t } = props.methods;
|
import { QRCode, Alert } from 'antd';
|
||||||
|
import Styles from './web.mobile.module.less';
|
||||||
|
import * as dayJs from 'dayjs';
|
||||||
|
import { CentToString } from 'oak-domain/lib/utils/money';
|
||||||
|
import { PAY_CHANNEL_OFFLINE_NAME, PAY_CHANNEL_WECHAT_APP_NAME, PAY_CHANNEL_WECHAT_H5_NAME, PAY_CHANNEL_WECHAT_JS_NAME, PAY_CHANNEL_WECHAT_MP_NAME, PAY_CHANNEL_WECHAT_NATIVE_NAME } from '../../../types/PayConfig';
|
||||||
|
import { PayCircleOutline, GlobalOutline } from 'antd-mobile-icons';
|
||||||
|
export function RenderOffline(props) {
|
||||||
|
const { pay, t, offline, updateMeta, metaUpdatable } = props;
|
||||||
|
const { meta, iState } = pay;
|
||||||
|
return (<>
|
||||||
|
<Form layout="horizontal" style={{ width: '100%', marginTop: 12 }}>
|
||||||
|
<Form.Item label={t("offline.label.tips")}>
|
||||||
|
<span style={{ wordBreak: 'break-all', textDecoration: 'underline' }}>{offline.tips}</span>
|
||||||
|
</Form.Item>
|
||||||
|
{!!(offline.options?.length) && <Form.Item label={t("offline.label.option")}>
|
||||||
|
<Selector value={meta?.option ? [meta?.option] : undefined} options={offline.options.map(ele => ({
|
||||||
|
label: ele,
|
||||||
|
value: ele,
|
||||||
|
}))} disabled={!metaUpdatable} onChange={(v) => updateMeta({
|
||||||
|
...meta,
|
||||||
|
option: v[0],
|
||||||
|
})}/>
|
||||||
|
</Form.Item>}
|
||||||
|
<Form.Item label={t("offline.label.serial")}>
|
||||||
|
<TextArea autoSize={{ minRows: 3 }} value={meta?.serial} disabled={!metaUpdatable} placeholder={metaUpdatable ? t('offline.placeholder.serial') : t('offline.placeholder.none')} onChange={(value) => updateMeta({
|
||||||
|
...meta,
|
||||||
|
serial: value,
|
||||||
|
})}/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</>);
|
||||||
|
}
|
||||||
|
function Counter(props) {
|
||||||
|
const { deadline } = props;
|
||||||
|
const [counter, setCounter] = useState('');
|
||||||
|
const timerFn = () => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now < deadline) {
|
||||||
|
const duration = dayJs.duration(deadline - now);
|
||||||
|
setCounter(duration.format('HH:mm:ss'));
|
||||||
|
setTimeout(timerFn, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
timerFn();
|
||||||
|
}, []);
|
||||||
|
return (<div className={Styles.counter}>{counter}</div>);
|
||||||
|
}
|
||||||
|
function RenderWechatPay(props) {
|
||||||
|
const { pay, t } = props;
|
||||||
|
const { externalId, channel, timeoutAt, iState } = pay;
|
||||||
|
switch (channel) {
|
||||||
|
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||||
|
if (iState === 'paying') {
|
||||||
|
return (<>
|
||||||
|
<Counter deadline={timeoutAt}/>
|
||||||
|
<QRCode value={externalId} size={280}/>
|
||||||
|
<div className={Styles.qrCodeTips}>
|
||||||
|
{process.env.NODE_ENV === 'production' ?
|
||||||
|
<Alert type="info" message={t('wechat.native.tips')}/> :
|
||||||
|
<Alert type="warning" message={t('wechat.native.tips2')}/>}
|
||||||
|
</div>
|
||||||
|
</>);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function RenderPayMeta(props) {
|
||||||
|
const { pay, application, t, payConfig, updateMeta } = props;
|
||||||
|
const { iState, channel } = pay;
|
||||||
|
if (['unpaid', 'paying'].includes(iState) && pay.applicationId !== application.id && channel !== PAY_CHANNEL_OFFLINE_NAME) {
|
||||||
|
return <Alert type='warning' message={t('notSameApp')}/>;
|
||||||
|
}
|
||||||
|
switch (channel) {
|
||||||
|
case PAY_CHANNEL_OFFLINE_NAME: {
|
||||||
|
const { '#oakLegalActions': legalActions } = pay;
|
||||||
|
const metaUpdatable = !!legalActions?.find(ele => typeof ele === 'object'
|
||||||
|
&& ele.action === 'update'
|
||||||
|
&& ele.attrs?.includes('meta'));
|
||||||
|
return (<>
|
||||||
|
{iState === 'paying' && <Alert type='info' message={t('offline.tips')}/>}
|
||||||
|
{RenderOffline({
|
||||||
|
pay,
|
||||||
|
t,
|
||||||
|
offline: payConfig.find(ele => ele.channel === PAY_CHANNEL_OFFLINE_NAME),
|
||||||
|
updateMeta,
|
||||||
|
metaUpdatable
|
||||||
|
})}
|
||||||
|
</>);
|
||||||
|
}
|
||||||
|
case PAY_CHANNEL_WECHAT_APP_NAME:
|
||||||
|
case PAY_CHANNEL_WECHAT_H5_NAME:
|
||||||
|
case PAY_CHANNEL_WECHAT_JS_NAME:
|
||||||
|
case PAY_CHANNEL_WECHAT_MP_NAME:
|
||||||
|
case PAY_CHANNEL_WECHAT_NATIVE_NAME: {
|
||||||
|
return <RenderWechatPay pay={pay} t={t}/>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
export default function Render(props) {
|
||||||
|
const { pay, application, iStateColor, payConfig, oakExecutable, onClose, closable } = props.data;
|
||||||
|
const { t, update, execute, clean } = props.methods;
|
||||||
|
if (pay && application) {
|
||||||
|
const { iState, channel, price, '#oakLegalActions': legalActions } = pay;
|
||||||
|
return (<div className={Styles.container}>
|
||||||
|
<Card title={t('title')} extra={<Tag color={iStateColor}>{t(`pay:v.iState.${iState}`)}</Tag>}>
|
||||||
|
<div>
|
||||||
|
<List>
|
||||||
|
<List.Item prefix={<PayCircleOutline />} extra={CentToString(price, 2)}>
|
||||||
|
{t('pay:attr.price')}
|
||||||
|
</List.Item>
|
||||||
|
<List.Item prefix={<GlobalOutline />} extra={t(`payChannel::${channel}`)}>
|
||||||
|
{t('pay:attr.channel')}
|
||||||
|
</List.Item>
|
||||||
|
</List>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<div className={Styles.meta}>
|
||||||
|
<RenderPayMeta pay={pay} t={t} application={application} payConfig={payConfig} updateMeta={(meta) => update({ meta })}/>
|
||||||
|
</div>
|
||||||
|
<div className={Styles.padding}/>
|
||||||
|
<div className={Styles.btn}>
|
||||||
|
{oakExecutable === true && (<>
|
||||||
|
<div className={Styles.btnItem}>
|
||||||
|
<Button block color='primary' onClick={() => execute()}>
|
||||||
|
{t('common::action.update')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className={Styles.btnItem}>
|
||||||
|
<Button block onClick={() => clean()}>
|
||||||
|
{t('common::reset')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</>)}
|
||||||
|
{closable && !(oakExecutable === true) && (<Button block color="primary" onClick={() => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: t('cc.title'),
|
||||||
|
content: t('cc.content'),
|
||||||
|
onConfirm: async () => {
|
||||||
|
await execute('close');
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}}>
|
||||||
|
{t('pay:action.close')}
|
||||||
|
</Button>)}
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
margin-top: 40px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.qrCodeTips {
|
||||||
|
margin-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter {
|
||||||
|
font-size: var(--oak-font-size-headline-medium);
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.btnItem {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,4 +15,5 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
|
||||||
iStateColor?: string;
|
iStateColor?: string;
|
||||||
payConfig?: PayConfig;
|
payConfig?: PayConfig;
|
||||||
onClose: () => undefined;
|
onClose: () => undefined;
|
||||||
|
closable: boolean;
|
||||||
}>): React.JSX.Element | null;
|
}>): React.JSX.Element | null;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export function RenderOffline(props) {
|
||||||
return (<>
|
return (<>
|
||||||
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 14 }} layout="horizontal" style={{ width: '100%', marginTop: 12 }}>
|
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 14 }} layout="horizontal" style={{ width: '100%', marginTop: 12 }}>
|
||||||
<Form.Item label={t("offline.label.tips")}>
|
<Form.Item label={t("offline.label.tips")}>
|
||||||
<span style={{ wordBreak: 'break-all' }}>{offline.tips}</span>
|
<span style={{ wordBreak: 'break-all', textDecoration: 'underline' }}>{offline.tips}</span>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t("offline.label.option")}>
|
<Form.Item label={t("offline.label.option")}>
|
||||||
<Select value={meta?.option} options={offline.options?.map(ele => ({
|
<Select value={meta?.option} options={offline.options?.map(ele => ({
|
||||||
|
|
@ -58,7 +58,7 @@ function RenderWechatPay(props) {
|
||||||
<Counter deadline={timeoutAt}/>
|
<Counter deadline={timeoutAt}/>
|
||||||
<QRCode value={externalId} size={280}/>
|
<QRCode value={externalId} size={280}/>
|
||||||
<div className={Styles.qrCodeTips}>
|
<div className={Styles.qrCodeTips}>
|
||||||
{process.env.NODE_ENV === 'production' ?
|
{process.env.NODE_ENV === 'production' ?
|
||||||
<Alert type="info" message={t('wechat.native.tips')}/> :
|
<Alert type="info" message={t('wechat.native.tips')}/> :
|
||||||
<Alert type="warning" message={t('wechat.native.tips2')}/>}
|
<Alert type="warning" message={t('wechat.native.tips2')}/>}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -103,11 +103,10 @@ function RenderPayMeta(props) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
export default function Render(props) {
|
export default function Render(props) {
|
||||||
const { pay, application, iStateColor, payConfig, oakExecutable, onClose } = props.data;
|
const { pay, application, iStateColor, payConfig, oakExecutable, onClose, closable } = props.data;
|
||||||
const { t, update, execute } = props.methods;
|
const { t, update, execute, clean } = props.methods;
|
||||||
if (pay && application) {
|
if (pay && application) {
|
||||||
const { iState, channel, price, '#oakLegalActions': legalActions } = pay;
|
const { iState, channel, price, '#oakLegalActions': legalActions } = pay;
|
||||||
const closable = !!legalActions?.find(ele => ele === 'close');
|
|
||||||
return (<Card title={t('title')} extra={<Tag color={iStateColor}>{t(`pay:v.iState.${iState}`)}</Tag>}>
|
return (<Card title={t('title')} extra={<Tag color={iStateColor}>{t(`pay:v.iState.${iState}`)}</Tag>}>
|
||||||
<div className={Styles.container}>
|
<div className={Styles.container}>
|
||||||
<div className={Styles.detail}>
|
<div className={Styles.detail}>
|
||||||
|
|
@ -128,9 +127,14 @@ export default function Render(props) {
|
||||||
<RenderPayMeta pay={pay} t={t} application={application} payConfig={payConfig} updateMeta={(meta) => update({ meta })}/>
|
<RenderPayMeta pay={pay} t={t} application={application} payConfig={payConfig} updateMeta={(meta) => update({ meta })}/>
|
||||||
</div>
|
</div>
|
||||||
<div className={Styles.btn}>
|
<div className={Styles.btn}>
|
||||||
{oakExecutable === true && (<Button onClick={() => execute()}>
|
{oakExecutable === true && (<>
|
||||||
{t('common::action.update')}
|
<Button type="primary" onClick={() => execute()}>
|
||||||
</Button>)}
|
{t('common::action.update')}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => clean()}>
|
||||||
|
{t('common::reset')}
|
||||||
|
</Button>
|
||||||
|
</>)}
|
||||||
{closable && !(oakExecutable === true) && (<Button type="primary" onClick={() => {
|
{closable && !(oakExecutable === true) && (<Button type="primary" onClick={() => {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: t('cc.title'),
|
title: t('cc.title'),
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ const attrUpdateMatrix = {
|
||||||
},
|
},
|
||||||
channel: PAY_CHANNEL_ACCOUNT_NAME,
|
channel: PAY_CHANNEL_ACCOUNT_NAME,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
paid: {
|
||||||
|
actions: ['succeedPaying'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ export const entityDesc = {
|
||||||
zh_CN: {
|
zh_CN: {
|
||||||
name: '订单',
|
name: '订单',
|
||||||
attr: {
|
attr: {
|
||||||
price: '订单金额',
|
price: '应支付金额',
|
||||||
paid: '已支付金额',
|
paid: '已支付金额',
|
||||||
refunded: '已退款金额',
|
refunded: '已退款金额',
|
||||||
iState: '支付状态',
|
iState: '支付状态',
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ const watchers = [
|
||||||
results.push(result);
|
results.push(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (results.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
return results.reduce((prev, cur) => mergeOperationResult(prev, cur));
|
return results.reduce((prev, cur) => mergeOperationResult(prev, cur));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ const attrUpdateMatrix = {
|
||||||
},
|
},
|
||||||
channel: PayConfig_1.PAY_CHANNEL_ACCOUNT_NAME,
|
channel: PayConfig_1.PAY_CHANNEL_ACCOUNT_NAME,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
paid: {
|
||||||
|
actions: ['succeedPaying'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ exports.entityDesc = {
|
||||||
zh_CN: {
|
zh_CN: {
|
||||||
name: '订单',
|
name: '订单',
|
||||||
attr: {
|
attr: {
|
||||||
price: '订单金额',
|
price: '应支付金额',
|
||||||
paid: '已支付金额',
|
paid: '已支付金额',
|
||||||
refunded: '已退款金额',
|
refunded: '已退款金额',
|
||||||
iState: '支付状态',
|
iState: '支付状态',
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,9 @@ const watchers = [
|
||||||
results.push(result);
|
results.push(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (results.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
return results.reduce((prev, cur) => (0, operationResult_1.mergeOperationResult)(prev, cur));
|
return results.reduce((prev, cur) => (0, operationResult_1.mergeOperationResult)(prev, cur));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<view>account detail</view>
|
||||||
|
|
@ -26,6 +26,9 @@ const attrUpdateMatrix: AttrUpdateMatrix<EntityDict> = {
|
||||||
},
|
},
|
||||||
channel: PAY_CHANNEL_ACCOUNT_NAME,
|
channel: PAY_CHANNEL_ACCOUNT_NAME,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
paid: {
|
||||||
|
actions: ['succeedPaying'],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,9 @@ const watchers: Watcher<EntityDict, 'pay', BRC>[] = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (results.length === 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
return results.reduce(
|
return results.reduce(
|
||||||
(prev, cur) => mergeOperationResult(prev, cur)
|
(prev, cur) => mergeOperationResult(prev, cur)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue