设计实现了sysAccountMove

This commit is contained in:
Xu Chang 2024-06-26 20:04:08 +08:00
parent ae185dd5a0
commit bd03f9bd67
33 changed files with 785 additions and 23 deletions

View File

@ -27,13 +27,16 @@ function checkAttributes(data) {
}
}
}
const { refundCompensateRatio, refundGapDays } = data;
const { refundCompensateRatio, refundGapDays, withdrawTransferLossRatio } = data;
if (typeof refundGapDays !== 'number' || refundGapDays < 0) {
throw new OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundGapDaysNotNegative');
}
if (typeof refundCompensateRatio !== 'number' || refundCompensateRatio < 0 || refundCompensateRatio > 100) {
throw new OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundCompensateIllegal');
}
if (typeof withdrawTransferLossRatio !== 'number' || withdrawTransferLossRatio < 0) {
throw new OakInputIllegalException('offlineAccount', ['withdrawTransferLossRatio'], 'offlineAccount::error.withdrawTransferLossRatioNotNegative');
}
}
const checkers = [
{

View File

@ -74,7 +74,7 @@ export default function render(props) {
update({ allowWithdrawTransfer });
}}/>
</Form.Item>
<Form.Item label={t('offlineAccount:attr.withdrawTransferLossRatio')} help={t('placeholder.withdrawTransferLossRatio')}>
<Form.Item label={t('offlineAccount:attr.withdrawTransferLossRatio')} help={t('placeholder.withdrawTransferLossRatio')} required>
<InputNumber value={offlineAccount.withdrawTransferLossRatio} max={5} min={0.01} addonAfter={"%"} step={0.01} precision={2} onChange={(value) => {
const withdrawTransferLossRatio = value;
update({ withdrawTransferLossRatio });

View File

@ -1,10 +1,11 @@
import React from 'react';
import React, { useState } from 'react';
import { Descriptions, Tag, Modal, Flex, Button, Divider, Alert } from 'antd';
import { AlipayOutlined, WechatOutlined } from '@ant-design/icons';
import Styles from './web.pc.module.less';
import { ThousandCont, ToYuan } from "oak-domain/lib/utils/money";
import { OfflineAccount as OADetail } from '../../offlineAccount/config/web.pc';
import { WpAccount as WADetail } from '../../wpAccount/config/web.pc';
import TransferList from '../transferList';
function OfflineAccount(props) {
const { data, t } = props;
const { type, channel, color, name } = data;
@ -65,6 +66,7 @@ export default function render(props) {
const { accounts, total, systemId, accountNum, accountTotalSum, accountAvailSum, refundCnt, refundPriceSum, transferCnt, transferPriceSum, orderCnt, orderPaidSum, orderRefundSum, } = props.data;
const { t, setMessage } = props.methods;
if (accounts && systemId) {
const [showTransferList, setShowTransferList] = useState(false);
return (<div className={Styles.container}>
<Descriptions title={t('sysAccount')} bordered items={[
{
@ -163,9 +165,12 @@ export default function render(props) {
<span className={Styles.symbol}>{t('common::pay.symbol')}</span>
<span className={Styles.value}>{ThousandCont(ToYuan(transferPriceSum || 0), 2)}</span>
</span>
<Button>
<Button onClick={() => setShowTransferList(true)}>
{t('transferList')}
</Button>
<Modal open={showTransferList} onCancel={() => setShowTransferList(false)} footer={null} closeIcon={null} width={800}>
<TransferList oakPath="$opb-sysAccount-survery-transferList"/>
</Modal>
</div>)
},
{

View File

@ -0,0 +1,3 @@
/// <reference types="wechat-miniprogram" />
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../oak-app-domain").EntityDict, "withdrawTransfer", true, WechatMiniprogram.Component.DataOption>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,52 @@
export default OakComponent({
entity: 'withdrawTransfer',
isList: true,
projection: {
id: 1,
price: 1,
loss: 1,
externalId: 1,
iState: 1,
withdrawAccount: {
org: 1,
name: 1,
code: 1,
channel: {
entity: 1,
entityId: 1,
offlineAccount: {
id: 1,
type: 1,
}
},
id: 1,
}
},
filters: [
{
filter() {
const systemId = this.features.application.getApplication().systemId;
return {
withdrawAccount: {
ofSystemId: systemId,
},
iState: 'transferring',
};
}
}
],
formData({ data }) {
return {
transfers: data?.map((ele) => {
const { withdrawAccount, ...rest } = ele;
const { entity, offlineAccount } = withdrawAccount.channel;
const channel = entity === 'offlineAccount' ? this.t(`withdraw::channel.offlineAccount.${offlineAccount?.type}`) : this.t(`withdraw::channel.${entity}`);
return {
...rest,
withdrawAccount,
channel,
};
}),
};
},
});

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,6 @@
{
"label": {
"channel": "转账渠道"
},
"tips": "正在转账的金额不计入任何账户,请到\"提现转账管理\"菜单完成操作"
}

View File

@ -0,0 +1,14 @@
import React from 'react';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
transfers?: (RowWithActions<EntityDict, 'withdrawTransfer'> & {
creatorName: string;
creatorMobile: string;
operatorName: string;
operatorMobile: string;
channel: string;
})[];
}, {
setUpdateId: (id: string, action: string) => void;
}>): React.JSX.Element;

View File

@ -0,0 +1,45 @@
import React from 'react';
import { Result, Alert } from 'antd';
import ListPro from 'oak-frontend-base/es/components/listPro';
import { ThousandCont, ToYuan } from 'oak-domain/lib/utils/money';
export default function Render(props) {
const { transfers, oakFullpath, oakExecutable } = props.data;
const { t, updateItem, execute, clean, setMessage, setUpdateId } = props.methods;
if (transfers?.length) {
return (<ListPro entity="withdrawTransfer" data={transfers} title={<Alert type="info" message={t('tips')}/>} attributes={[
{
path: 'price',
label: t('withdrawTransfer:attr.price'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row.price), 2)}`;
}
},
{
path: 'loss',
label: t('withdrawTransfer:attr.loss'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row.loss), 2)}`;
}
},
{
path: '$$createAt$$',
label: t('common::$$createAt$$'),
width: 100,
},
{
path: 'channel',
label: t('label.channel'),
type: 'string',
width: 100,
},
{
path: 'externalId',
label: t('withdrawTransfer:attr.externalId'),
width: 120,
},
]} disabledOp={true}/>);
}
return (<Result status="404" title={t('common::noData')}/>);
}

View File

@ -1,6 +1,6 @@
{
"label": {
"channel": "提现渠道",
"channel": "转账渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",

View File

@ -409,6 +409,19 @@ const i18ns = [
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额"
}
},
{
id: "207fd2189d3697d15672176ae88f4175",
namespace: "oak-pay-business-c-sysAccount-transferList",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/sysAccount/transferList",
data: {
"label": {
"channel": "转账渠道"
},
"tips": "正在转账的金额不计入任何账户,请到\"提现转账管理\"菜单完成操作"
}
},
{
id: "79255be8c093dfef9765b3f367cab553",
namespace: "oak-pay-business-c-wechatPay-upsert",
@ -583,7 +596,7 @@ const i18ns = [
position: "src/components/withdrawTransfer/list",
data: {
"label": {
"channel": "提现渠道",
"channel": "转账渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",
@ -786,7 +799,8 @@ const i18ns = [
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}
},

View File

@ -22,6 +22,7 @@
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}

View File

@ -30,13 +30,16 @@ function checkAttributes(data) {
}
}
}
const { refundCompensateRatio, refundGapDays } = data;
const { refundCompensateRatio, refundGapDays, withdrawTransferLossRatio } = data;
if (typeof refundGapDays !== 'number' || refundGapDays < 0) {
throw new types_1.OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundGapDaysNotNegative');
}
if (typeof refundCompensateRatio !== 'number' || refundCompensateRatio < 0 || refundCompensateRatio > 100) {
throw new types_1.OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundCompensateIllegal');
}
if (typeof withdrawTransferLossRatio !== 'number' || withdrawTransferLossRatio < 0) {
throw new types_1.OakInputIllegalException('offlineAccount', ['withdrawTransferLossRatio'], 'offlineAccount::error.withdrawTransferLossRatioNotNegative');
}
}
const checkers = [
{

View File

@ -411,6 +411,19 @@ const i18ns = [
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额"
}
},
{
id: "207fd2189d3697d15672176ae88f4175",
namespace: "oak-pay-business-c-sysAccount-transferList",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/sysAccount/transferList",
data: {
"label": {
"channel": "转账渠道"
},
"tips": "正在转账的金额不计入任何账户,请到\"提现转账管理\"菜单完成操作"
}
},
{
id: "79255be8c093dfef9765b3f367cab553",
namespace: "oak-pay-business-c-wechatPay-upsert",
@ -585,7 +598,7 @@ const i18ns = [
position: "src/components/withdrawTransfer/list",
data: {
"label": {
"channel": "提现渠道",
"channel": "转账渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",
@ -788,7 +801,8 @@ const i18ns = [
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}
},

View File

@ -22,6 +22,7 @@
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}

View File

@ -33,13 +33,16 @@ function checkAttributes(data: EntityDict['offlineAccount']['CreateSingle']['dat
}
}
const { refundCompensateRatio, refundGapDays } = data;
const { refundCompensateRatio, refundGapDays, withdrawTransferLossRatio } = data;
if (typeof refundGapDays !== 'number' || refundGapDays < 0) {
throw new OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundGapDaysNotNegative');
}
if (typeof refundCompensateRatio !== 'number' || refundCompensateRatio < 0 || refundCompensateRatio > 100) {
throw new OakInputIllegalException('offlineAccount', ['refundGapDays'], 'offlineAccount::error.refundCompensateIllegal');
}
if (typeof withdrawTransferLossRatio !== 'number' || withdrawTransferLossRatio < 0) {
throw new OakInputIllegalException('offlineAccount', ['withdrawTransferLossRatio'], 'offlineAccount::error.withdrawTransferLossRatioNotNegative');
}
}
const checkers: Checker<EntityDict, 'offlineAccount', RuntimeCxt>[] = [

View File

@ -176,6 +176,7 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
<Form.Item
label={t('offlineAccount:attr.withdrawTransferLossRatio')}
help={t('placeholder.withdrawTransferLossRatio')}
required
>
<InputNumber
value={offlineAccount.withdrawTransferLossRatio}

View File

@ -24,5 +24,6 @@
"orderPriceSum": "订单总额",
"orderManage": "订单管理",
"orderList": "订单列表",
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额"
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额",
"move": "移动资金"
}

View File

@ -1,12 +1,15 @@
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
import React from 'react';
import React, { useState } from 'react';
import { Descriptions, Tag, Form, Switch, Modal, Input, Select, Flex, Button, Divider, Alert } from 'antd';
import { AlipayOutlined, WechatOutlined } from '@ant-design/icons';
import Styles from './web.pc.module.less';
import { ThousandCont, ToYuan } from "oak-domain/lib/utils/money";
import { OfflineAccount as OADetail } from '../../offlineAccount/config/web.pc';
import { WpAccount as WADetail } from '../../wpAccount/config/web.pc';
import TransferList from '../transferList';
import SysAccountMoveCreate from '../../sysAccountMove/create';
function OfflineAccount(props: {
data: EntityDict['offlineAccount']['OpSchema'] & { color?: string };
@ -161,6 +164,8 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
const { t, setMessage } = props.methods;
if (accounts && systemId) {
const [showTransferList, setShowTransferList] = useState(false);
const [showMoveCreate, setShowMoveCreate] = useState(false);
return (
<div className={Styles.container}>
<Descriptions
@ -180,6 +185,31 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
}
]}
column={2}
extra={
<>
<Button
type="primary"
onClick={() => setShowMoveCreate(true)}
>
{t('move')}
</Button>
<Modal
destroyOnClose
open={showMoveCreate}
onCancel={() => setShowMoveCreate(false)}
closeIcon={null}
width={800}
title={t('move')}
footer={null}
>
<SysAccountMoveCreate
oakPath="$opb-sysAccount-survery-saMoveCreate"
systemId={systemId}
onSuccess={() => setShowMoveCreate(false)}
/>
</Modal>
</>
}
/>
<div className={Styles.sysAccounts}>
{
@ -243,7 +273,7 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
{accountNum && <Descriptions
title={t('business')}
bordered
extra={<Alert type="info" message={t('tips1')}/>}
extra={<Alert type="info" message={t('tips1')} />}
items={[
{
label: t('accountTotalSum'),
@ -295,9 +325,22 @@ export default function render(props: WebComponentProps<EntityDict, 'offlineAcco
<span className={Styles.symbol}>{t('common::pay.symbol')}</span>
<span className={Styles.value}>{ThousandCont(ToYuan(transferPriceSum || 0), 2)}</span>
</span>
<Button>
<Button
onClick={() => setShowTransferList(true)}
>
{t('transferList')}
</Button>
<Modal
open={showTransferList}
onCancel={() => setShowTransferList(false)}
footer={null}
closeIcon={null}
width={800}
>
<TransferList
oakPath="$opb-sysAccount-survery-transferList"
/>
</Modal>
</div>
)
},

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,54 @@
export default OakComponent({
entity: 'withdrawTransfer',
isList: true,
projection: {
id: 1,
price: 1,
loss: 1,
externalId: 1,
iState: 1,
withdrawAccount: {
org: 1,
name: 1,
code: 1,
channel: {
entity: 1,
entityId: 1,
offlineAccount: {
id: 1,
type: 1,
}
},
id: 1,
}
},
filters: [
{
filter() {
const systemId = this.features.application.getApplication()!.systemId;
return {
withdrawAccount: {
ofSystemId: systemId,
},
iState: 'transferring',
};
}
}
],
formData({ data }) {
return {
transfers: data?.map(
(ele) => {
const { withdrawAccount, ...rest } = ele;
const { entity, offlineAccount } = withdrawAccount!.channel!;
const channel = entity === 'offlineAccount' ? this.t(`withdraw::channel.offlineAccount.${offlineAccount?.type}`) : this.t(`withdraw::channel.${entity}`);
return {
...rest,
withdrawAccount,
channel,
};
}
),
};
},
});

View File

@ -0,0 +1,6 @@
{
"label": {
"channel": "转账渠道"
},
"tips": "正在转账的金额不计入任何账户,请到\"提现转账管理\"菜单完成操作"
}

View File

@ -0,0 +1,72 @@
import React, { useState } from 'react';
import { Result, Alert } from 'antd';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../oak-app-domain';
import ListPro from 'oak-frontend-base/es/components/listPro';
import { ThousandCont, ToYuan } from 'oak-domain/lib/utils/money';
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
transfers?: (RowWithActions<EntityDict, 'withdrawTransfer'> & {
creatorName: string;
creatorMobile: string;
operatorName: string;
operatorMobile: string;
channel: string;
})[];
}>) {
const { transfers, oakFullpath, oakExecutable } = props.data;
const { t } = props.methods;
if (transfers?.length) {
return (
<ListPro
entity="withdrawTransfer"
data={transfers}
title={
<Alert type="info" message={t('tips')} />
}
attributes={[
{
path: 'price',
label: t('withdrawTransfer:attr.price'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row!.price), 2)}`
}
},
{
path: 'loss',
label: t('withdrawTransfer:attr.loss'),
width: 100,
render: (row) => {
return `${t('common::pay.symbol')} ${ThousandCont(ToYuan(row!.loss), 2)}`
}
},
{
path: '$$createAt$$',
label: t('common::$$createAt$$'),
width: 100,
},
{
path: 'channel',
label: t('label.channel'),
type: 'string',
width: 100,
},
{
path: 'externalId',
label: t('withdrawTransfer:attr.externalId'),
width: 120,
},
]}
disabledOp={true}
/>
)
}
return (
<Result
status="404"
title={t('common::noData')}
/>
);
}

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,172 @@
import { uniq } from "oak-domain/lib/utils/lodash";
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { ThousandCont, ToCent, ToYuan } from 'oak-domain/lib/utils/money';
import { EntityDict } from "../../../oak-app-domain";
export default OakComponent({
properties: {
systemId: '',
entities: [] as string[],
onSuccess: (id: string) => undefined as void,
},
data: {
accounts: [] as Array<{
id: string;
entity: string;
price: number;
priceStr: string;
label: string;
name?: string;
}>,
fromEntity: '',
fromEntityId: '',
toEntity: '',
toEntityId: '',
price: 0,
externalId: '',
remark: '',
max: 0,
},
lifetimes: {
ready() {
this.refreshSysAccount();
}
},
methods: {
async refreshSysAccount() {
const { entities, systemId } = this.props;
const accounts2 = await Promise.all(
uniq(['wpAccount', 'offlineAccount'].concat(entities || [])).map(
async (entity) => {
const { data: rows } = await this.features.cache.refresh(entity as 'offlineAccount', {
data: entity === 'offlineAccount' ? {
id: 1,
price: 1,
name: 1,
type: 1,
channel: 1,
} : {
id: 1,
price: 1,
},
filter: {
systemId,
}
});
const accounts = rows.map(
(row) => {
const { id, price, name, type, channel } = row;
return {
id: id!,
entity: entity!,
priceStr: ThousandCont(ToYuan(price!))!,
price: price!,
label: entity === 'offlineAccount' ? `${this.t(`${entity}:name`)}-${this.t(`offlineAccount:v.type.${type}`)}` : this.t(`${entity}:name`),
name: name || undefined,
};
}
);
return accounts;
}
)
);
this.setState({
accounts: accounts2.flat(),
});
},
setFromId(id: string) {
const { accounts } = this.state;
const account = accounts!.find(
ele => ele.id === id
);
this.setState({
fromEntity: account!.entity,
fromEntityId: account!.id,
max: ToYuan(account!.price!),
});
},
setToId(id: string) {
const { accounts } = this.state;
const account = accounts!.find(
ele => ele.id === id
);
this.setState({
toEntity: account!.entity,
toEntityId: account!.id,
});
},
setPrice(price: number) {
this.setState({
price,
});
},
setExternalId(externalId: string) {
this.setState({
externalId,
});
},
setRemark(remark: string) {
this.setState({
remark,
});
},
async createMove() {
const { fromEntity, fromEntityId, toEntity, toEntityId, price, remark, externalId} = this.state;
const userId = this.features.token.getUserId();
const systemId = this.features.application.getApplication()!.systemId;
const id = await generateNewIdAsync();
const price2 = ToCent(price);
const { onSuccess } = this.props;
await this.execute(undefined, undefined, undefined, [
{
entity: 'sysAccountMove',
operation: {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
price: price2,
externalId,
remark,
systemId,
operatorId: userId,
sysAccountOper$sysAccountMove: [
{
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
entity: fromEntity,
entityId: fromEntityId,
delta: -price2,
type: 'moveOut',
},
},
{
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
entity: toEntity,
entityId: toEntityId,
delta: price2,
type: 'moveIn',
},
},
]
}
} as EntityDict['sysAccountMove']['CreateSingle'],
}
]);
onSuccess && onSuccess(id);
}
}
});

View File

@ -0,0 +1,6 @@
{
"label": {
"from": "从账户",
"to": "到账户"
}
}

View File

@ -0,0 +1,169 @@
import React, { useState } from 'react';
import { Button, Alert, Form, Input, InputNumber, Radio, Space, Flex } from 'antd';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../oak-app-domain';
import ListPro from 'oak-frontend-base/es/components/listPro';
import { ThousandCont, ToYuan } from 'oak-domain/lib/utils/money';
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
fromEntityId?: string;
toEntityId?: string;
price: number;
max?: number;
externalId: string;
remark: string;
accounts: Array<{
id: string;
entity: string;
price: number;
priceStr: string;
label: string;
name?: string;
}>;
}, {
setFromId: (id: string) => void;
setToId: (id: string) => void;
setPrice: (price: number) => void;
setExternalId: (externalId: string) => void;
setRemark: (remark: string) => void;
createMove: () => Promise<void>;
}>) {
const { price, externalId, max, remark, fromEntityId, toEntityId, accounts } = props.data;
const { t, setFromId, setToId, setPrice, setExternalId, setRemark, createMove } = props.methods;
if (accounts.length) {
return (
<>
<Form
labelCol={{ span: 4 }}
wrapperCol={{ span: 14 }}
layout="horizontal"
style={{
marginTop: 20,
}}
>
<Form.Item
label={t('label.from')}
required
>
<Radio.Group onChange={({ target }) => {
setFromId(target.value);
}} value={fromEntityId}>
<Space
direction="vertical"
>
{
accounts.filter(
ele => ele.price > 0
).map(
ele => {
const { id, label, price, priceStr } = ele;
return (
<Radio
value={id}
key={id}
disabled={price <= 0}
>
{`${label}: ${t('common::pay.symbol')}${priceStr}`}
</Radio>
);
}
)
}
</Space>
</Radio.Group>
</Form.Item>
{
fromEntityId && <Form.Item
label={t('label.to')}
required
>
<Radio.Group onChange={({ target }) => {
setToId(target.value);
}} value={toEntityId}>
<Space
direction="vertical"
>
{
accounts.filter(
ele => ele.id !== fromEntityId
).map(
ele => {
const { id, label, price, priceStr } = ele;
return (
<Radio
value={id}
key={id}
>
{`${label}: ${t('common::pay.symbol')}${priceStr}`}
</Radio>
);
}
)
}
</Space>
</Radio.Group>
</Form.Item>
}
{
fromEntityId && toEntityId && <Form.Item
label={t('sysAccountMove:attr.price')}
required
>
<InputNumber
value={price}
style={{
width: 240,
}}
max={max}
precision={2}
onChange={(value) => {
const price = value as number;
setPrice(price)
}}
/>
</Form.Item>
}
{
fromEntityId && toEntityId && <Form.Item
label={t('sysAccountMove:attr.externalId')}
required
>
<Input
value={externalId}
onChange={({ currentTarget }) => {
const externalId = currentTarget.value;
setExternalId(externalId);
}}
maxLength={64}
/>
</Form.Item>
}
{
fromEntityId && toEntityId && <Form.Item label={t('sysAccountMove:attr.remark')}>
<Input.TextArea
value={remark || undefined}
onChange={({ currentTarget }) => {
const remark = currentTarget.value;
setRemark(remark);
}}
rows={4}
/>
</Form.Item>
}
</Form>
<Flex style={{ width: '100%', padding: 12 }} justify="end">
<Button
type="primary"
disabled={!fromEntityId || !toEntityId || !price || !externalId}
onClick={() => createMove()}
>
{t('common::confirm')}
</Button>
</Flex>
</>
);
}
return null;
}

View File

@ -1,6 +1,6 @@
{
"label": {
"channel": "提现渠道",
"channel": "转账渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",

View File

@ -27,7 +27,7 @@ export default function render(props: WebComponentProps<EntityDict, 'wpAccount',
systemId={systemId}
key="wpCreate"
/>}
{(wpAccount.wechatPayId && wechatPay?.$$createAt$$ !== 1) && <WechatPayUpsert
{(wpAccount.wechatPayId && wpAccount?.$$createAt$$ !== 1) && <WechatPayUpsert
oakPath={`${oakFullpath}.wechatPay`}
systemId={systemId}
key="wpUpdate"

View File

@ -408,7 +408,34 @@ const i18ns: I18n[] = [
"orderPriceSum": "订单总额",
"orderManage": "订单管理",
"orderList": "订单列表",
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额"
"tips1": "总账户余额+退款总额+转账总额+订单总额应该等于系统账户总余额",
"move": "移动资金"
}
},
{
id: "207fd2189d3697d15672176ae88f4175",
namespace: "oak-pay-business-c-sysAccount-transferList",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/sysAccount/transferList",
data: {
"label": {
"channel": "转账渠道"
},
"tips": "正在转账的金额不计入任何账户,请到\"提现转账管理\"菜单完成操作"
}
},
{
id: "17a11f164c9977d8eb5b59fbc78755d5",
namespace: "oak-pay-business-c-sysAccountMove-create",
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/sysAccountMove/create",
data: {
"label": {
"from": "从账户",
"to": "到账户"
}
}
},
{
@ -585,7 +612,7 @@ const i18ns: I18n[] = [
position: "src/components/withdrawTransfer/list",
data: {
"label": {
"channel": "提现渠道",
"channel": "转账渠道",
"transferActualPrice": "实际转账金额",
"sysAccountAmount": "系统账户余额",
"cn": "创建者",
@ -788,7 +815,8 @@ const i18ns: I18n[] = [
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}
},

View File

@ -0,0 +1,39 @@
import {
String,
Text,
Price,
Boolean,
Datetime,
} from 'oak-domain/lib/types/DataType';
import { EntityShape } from 'oak-domain/lib/types/Entity';
import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as User } from './User';
import { Schema as System } from './System';
export interface Schema extends EntityShape {
price: Price;
externalId: String<64>;
operator: User;
remark?: Text;
system: System;
};
export const entityDesc: EntityDesc<Schema> = {
locales: {
zh_CN: {
name: '系统资金移动',
attr: {
price: '金额',
externalId: '外部流水号',
operator: '操作者',
remark: '备注',
system: '所属系统'
},
},
},
configuration: {
actionType: 'appendOnly',
}
}

View File

@ -11,6 +11,7 @@ import { EntityDesc, ActionDef } from 'oak-domain/lib/types';
import { Schema as Pay } from './Pay';
import { Schema as Refund } from './Refund';
import { Schema as WithdrawTransfer } from './WithdrawTransfer';
import { Schema as SysAccountMove} from './SysAccountMove';
export interface Schema extends EntityShape {
delta: Price;
@ -20,6 +21,7 @@ export interface Schema extends EntityShape {
pay?: Pay;
refund?: Refund;
withdrawTransfer?: WithdrawTransfer;
sysAccountMove?: SysAccountMove;
};
export const entityDesc: EntityDesc<Schema, '', '', {
@ -36,6 +38,7 @@ export const entityDesc: EntityDesc<Schema, '', '', {
pay: '关联支付',
refund: '关联退款',
withdrawTransfer: '关联转账提现',
sysAccountMove: '系统资金移动'
},
v: {
type: {

View File

@ -22,6 +22,7 @@
"error": {
"nameQrCodeBothNull": "账号名和二维码不能同时为空",
"refundGapDaysNotNegative": "允许退款天数不能为负数或空",
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间"
"refundCompensateIllegal": "退款补偿百分比必须在0到100之间",
"withdrawTransferLossRatioNotNegative": "提现转账费率必须在0到100之间"
}
}