Merge branch 'dev' into release

This commit is contained in:
Xu Chang 2024-06-19 16:13:38 +08:00
commit ef2db38f00
76 changed files with 1178 additions and 160 deletions

View File

@ -3,7 +3,7 @@ export declare function getWithdrawCreateData(params: {
accountId: string;
price: number;
withdrawAccountId?: string;
}, context: BRC): Promise<(Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}, context: BRC): Promise<(Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
accountId: string;
@ -17,7 +17,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
accountId: string;
@ -31,7 +31,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
account?: undefined;
@ -45,7 +45,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
account?: undefined;

View File

@ -19,6 +19,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'account', f
textColor: string;
priceColor: string;
bgColor: string;
onGoToHistory: () => void;
}, {
newDeposit: () => Promise<void>;
setDepositOpen: (v: boolean) => void;
@ -29,5 +30,4 @@ export default function Render(props: WebComponentProps<EntityDict, 'account', f
onDepositClick: () => void;
onDepositModalClose: () => void;
onUnfinishedDepositClick: () => void;
onGoToHistory: () => void;
}>): React.JSX.Element | null;

View File

@ -5,8 +5,8 @@ import classNames from 'classnames';
import { CentToString } from 'oak-domain/lib/utils/money';
import NewDeposit from '../../deposit/new';
export default function Render(props) {
const { account, depositMaxCent, newDepositPath, depositOpen, depositMinCent, ufOpen, depPrice, depositChannel, depositLoss, depositing, onWithdraw, textColor, priceColor, bgColor, } = props.data;
const { t, newDeposit, setMessage, setDepositOpen, setUfOpen, setDepPrice, setDepositChannel, onUnfinishedDepositClick, setDepositing, onDepositClick, onDepositModalClose, onGoToHistory, } = props.methods;
const { account, depositMaxCent, newDepositPath, depositOpen, depositMinCent, ufOpen, depPrice, depositChannel, depositLoss, depositing, onWithdraw, textColor, priceColor, bgColor, onGoToHistory } = props.data;
const { t, newDeposit, setMessage, setDepositOpen, setUfOpen, setDepPrice, setDepositChannel, onUnfinishedDepositClick, setDepositing, onDepositClick, onDepositModalClose, } = props.methods;
if (account) {
const { total, avail, '#oakLegalActions': legalActions, accountOper$account: opers } = account;
return (<div className={Styles.container}>

View File

@ -16,6 +16,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'account', f
depositMeta: any;
depositing: boolean;
newDepositPath: string;
onGoToHistory: () => void;
}, {
newDeposit: () => Promise<string>;
setDepositOpen: (v: boolean) => void;
@ -26,5 +27,4 @@ export default function Render(props: WebComponentProps<EntityDict, 'account', f
onDepositClick: () => void;
onDepositModalClose: () => void;
onUnfinishedDepositClick: () => void;
onGoToHistory: () => void;
}>): React.JSX.Element | null;

View File

@ -7,8 +7,8 @@ import NewDeposit from '../../deposit/new';
import AccountOperList from '../../accountOper/pure/List.pc';
import { AccountBookOutlined, ScheduleOutlined } from '@ant-design/icons';
export default function Render(props) {
const { account, depositMaxCent, newDepositPath, depositOpen, depositMinCent, ufOpen, depPrice, depositChannel, depositLoss, depositing, onWithdraw, } = props.data;
const { t, newDeposit, setMessage, setDepositOpen, setUfOpen, setDepPrice, setDepositChannel, onUnfinishedDepositClick, setDepositing, onDepositClick, onDepositModalClose, onGoToHistory, } = props.methods;
const { account, depositMaxCent, newDepositPath, depositOpen, depositMinCent, ufOpen, depPrice, depositChannel, depositLoss, depositing, onWithdraw, onGoToHistory, } = props.data;
const { t, newDeposit, setMessage, setDepositOpen, setUfOpen, setDepPrice, setDepositChannel, onUnfinishedDepositClick, setDepositing, onDepositClick, onDepositModalClose, } = props.methods;
if (account) {
const { total, avail, '#oakLegalActions': legalActions, accountOper$account: opers } = account;
return (<>

View File

@ -6,22 +6,28 @@ export default OakComponent({
projection: {
id: 1,
type: 1,
totalPlus: 1,
avail: 1,
availPlus: 1,
$$createAt$$: 1,
},
properties: {
accountId: '',
},
data: {
month: new Date(),
monthStr: '',
type: 'both',
chooseMonth: false,
chooseType: false,
pickerStart: dayjs().subtract(10, 'y').startOf('m').format('YYYY-MM-DD'),
pickerEnd: dayjs().startOf('m').format('YYYY-MM-DD'),
},
filters: [
{
filter() {
const { accountId } = this.props;
return {
accountId,
avail: {
$ne: 0,
},
};
}
}
@ -38,19 +44,125 @@ export default OakComponent({
const sign = availPlus > 0 ? '+' : '-';
const time = dayjs($$createAt$$).format('YYYY-MM-DD HH:mm');
const symbol = this.t(`accountOper:v.type.${type}`)[0];
const bgColor = this.features.style.getColor('accountOper', 'type', type);
return {
value: `${sign}${plus}`,
time,
type,
type: this.t(`accountOper:v.type.${type}`),
symbol,
avail: ThousandCont(ToYuan(avail), 2)
avail: ThousandCont(ToYuan(avail), 2),
bgColor,
};
}),
};
},
lifetimes: {
ready() {
this.setTypeFilter();
}
},
methods: {
loadMoreMp() {
return this.loadMore();
},
setType(type) {
this.setState({
type,
}, () => this.setTypeFilter());
},
setTypeFilter() {
const { type } = this.state;
switch (type) {
case 'in': {
this.addNamedFilter({
'#name': 'type',
filter: {
availPlus: {
$gt: 0,
},
}
}, true);
break;
}
case 'out': {
this.addNamedFilter({
'#name': 'type',
filter: {
availPlus: {
$lt: 0,
},
}
}, true);
break;
}
case 'both':
default: {
this.addNamedFilter({
'#name': 'type',
filter: {
availPlus: {
$ne: 0,
},
}
}, true);
break;
}
}
},
setMonth(month) {
if (month) {
const m = dayjs(month);
const begin = m.startOf('month').valueOf();
const end = m.endOf('month').valueOf();
this.setState({
monthStr: m.format('YYYY-MM'),
month,
}, () => this.addNamedFilter({
'#name': 'month',
filter: {
$$createAt$$: {
$between: [begin, end],
}
}
}, true));
}
else {
this.setState({
monthStr: '',
month: undefined,
}, () => this.removeNamedFilter({
'#name': 'month',
filter: {}
}, true));
}
},
setChooseMonth(chooseMonth) {
this.setState({ chooseMonth });
},
setChooseType(chooseType) {
this.setState({ chooseType });
},
setMonthMp(e) {
const { value } = e.detail;
const month = dayjs(value).valueOf();
this.setMonth(month);
},
openChooseTypeMp() {
this.setState({
chooseType: true,
});
},
closeChooseTypeMp() {
this.setState({
chooseType: false,
});
},
setTypeMp({ detail }) {
const { key, } = detail;
this.setState({
type: key,
}, () => this.setTypeFilter());
this.closeChooseTypeMp();
}
}
});

View File

@ -1 +1,10 @@
{}
{
"usingComponents": {
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index",
"l-popup": "@oak-frontend-base/miniprogram_npm/lin-ui/popup/index",
"l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index",
"l-radio-group": "@oak-frontend-base/miniprogram_npm/lin-ui/radio-group/index",
"l-radio": "@oak-frontend-base/miniprogram_npm/lin-ui/radio/index",
"oak-icon": "@oak-frontend-base/components/icon/index"
}
}

View File

@ -0,0 +1,125 @@
@import '../../../config/styles/mp/index.less';
@import '../../../config/styles/mp/mixins.less';
.myScroll {
height: 100%;
display: flex;
flex-direction: column;
}
.header {
position: sticky;
top: 0rpx;
left: 0rpx;
width: 100%;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
box-sizing: border-box;
background-color: @oak-bg-color-page;
color: @oak-text-color-primary;
}
.headerItem {
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.list {
flex: 1;
background-color: #fff;
}
.my-list-class {
border-bottom: 2rpx solid @oak-border-color;
}
.left {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 24rpx 0rpx;
}
.right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 16rpx;
padding: 24rpx 0rpx;
margin-left: 12rpx;
}
.avatar {
font-size: 40rpx;
font-weight: bold;
width: 96rpx;
height: 96rpx;
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
color: @oak-text-color-primary;
}
.middle {
margin-left: 20rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 16rpx;
}
.type {
font-weight: bold;
font-size: 32rpx;
}
.sub {
color: #888;
font-size: 26rpx;
}
.empty {
height: calc(100% - 90rpx);
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
gap: 24rpx;
}
.icon {
color: @oak-color-primary;
}
.btn {
color: #333;
border: 2rpx solid #eee;
}
.container {
background-color: #fff;
height: 15vh;
padding: 32rpx;
box-sizing: border-box;
}
.radio-group-class {
width: 100%;
display: grid;
grid-column: 3;
grid-template-columns: 1fr 1fr 1fr;
}

View File

@ -0,0 +1,56 @@
<scroll-view scroll-y bindscrolltolower="loadMoreMp" class="myScroll" refresher-enabled="{{true}}"
bindrefresherrefresh="bindrefresherrefresh" refresher-triggered="{{refreshing}}">
<view class="header">
<picker mode="date" fields="month" value="{{monthStr}}" start="{{pickerStart}}" end="{{pickerEnd}}" bindchange="setMonthMp">
<view class="headerItem">
<span>{{monthStr || t('chooseMonth')}}</span>
<oak-icon name="unfold" size="20" color="#000"/>
</view>
</picker>
<view class="headerItem" bindtap="openChooseTypeMp">
<oak-icon name="search" size="20" color="#000" />
<span>{{t('type.'+type)}}</span>
</view>
</view>
<view wx:if="{{accountOpers && accountOpers.length>0}}" class="list">
<l-list wx:for="{{accountOpers}}" wx:key="id" gap="24" is-link="{{false}}" l-class="my-list-class">
<view slot="left-section" class="left">
<view class="avatar" style="background-color:{{item.bgColor}}">{{item.symbol}}</view>
<view class="middle">
<view class="type">{{item.type}}</view>
<view class="sub">{{item.time}}</view>
</view>
</view>
<view slot="right-section" class="right">
<view style="color:{{item.bgColor}}">{{item.value}}</view>
<view class="sub">{{t('account:attr.avail')}}{{item.avail}}</view>
</view>
</l-list>
</view>
<view wx:else class="empty">
<view class="content">
<oak-icon name="refresh" size="48" class="icon"/>
<view>{{t('common::noData')}}</view>
<l-button
plain="true"
bind:lintap="navigateBack"
l-class="btn"
size="long"
width="160"
height="80"
>
{{t('common::back')}}
</l-button>
</view>
</view>
</scroll-view>
<l-popup show="{{chooseType}}" content-align="bottom" bind:lintap="closeChooseTypeMp" >
<view class="container">
<l-radio-group current="{{type}}" bind:linchange="setTypeMp" none-checked="{{false}}" placement="row" l-class="radio-group-class">
<l-radio key="both">{{t('type.both')}}</l-radio>
<l-radio key="in">{{t('type.in')}}</l-radio>
<l-radio key="out">{{t('type.out')}}</l-radio>
</l-radio-group>
</view>
</view>
</l-popup>

View File

@ -1 +1,9 @@
{}
{
"history": "账户历史",
"chooseMonth": "选择月份",
"type": {
"both": "全部",
"in": "仅收入",
"out": "仅支出"
}
}

View File

@ -4,4 +4,52 @@
min-height: 100%;
flex-direction: column;
align-items: stretch;
.control {
padding: 4px;
}
.item {
padding-bottom: 10px;
.symbol {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 25px;
margin: 10px;
font-size: large;
font-weight: bold;
}
.main {
margin-top: 5px;
width: 100%;
}
.sub {
width: 100%;
.type {
font-weight: bold;
}
}
}
}
.types {
padding: 20px;
width: 100%;
}
.empty {
display: flex;
width: 100%;
flex: 1;
background-color: white;
align-items: center;
justify-content: center;
}

View File

@ -8,6 +8,17 @@ export default function Render(props: WebComponentProps<EntityDict, 'accountOper
type: string;
symbol: string;
avail: string;
bgColor: string;
}>;
hasMore: boolean;
}>): React.JSX.Element | null;
month?: Date;
monthStr: string;
type: 'in' | 'out' | 'both';
chooseMonth: boolean;
chooseType: boolean;
}, {
setMonth: (month?: number | Date) => void;
setType: (type: 'in' | 'out' | 'both') => void;
setChooseMonth: (cm: boolean) => void;
setChooseType: (ct: boolean) => void;
}>): React.JSX.Element;

View File

@ -1,29 +1,63 @@
import React from 'react';
import { List, InfiniteScroll, Space } from 'antd-mobile';
import { List, InfiniteScroll, Space, DatePicker, Button, Popup, Radio, Result } from 'antd-mobile';
import { DownOutline, SearchOutline, UndoOutline } from 'antd-mobile-icons';
import Styles from './mobile.module.less';
export default function Render(props) {
const { accountOpers, hasMore } = props.data;
const { t, loadMore } = props.methods;
if (accountOpers?.length) {
return (<div className={Styles.container}>
<List>
{accountOpers.map((oper, idx) => {
const { value, time, type, symbol, avail } = oper;
return (<List.Item key={idx} prefix={<div className={Styles.symbol}>
{symbol}
</div>} description={<Space direction="horizontal" justify="between">
<span>{time}</span>
<span>{t('accountOper:attr.avail')}:{avail}</span>
</Space>}>
<Space direction="horizontal" justify="between">
<span>{type}</span>
<span>{value}</span>
</Space>
</List.Item>);
const { accountOpers, hasMore, month, type, chooseMonth, chooseType, monthStr } = props.data;
const { t, loadMore, setType, setMonth, setChooseMonth, setChooseType, navigateBack } = props.methods;
return (<div className={Styles.container}>
<Space justify="between" className={Styles.control}>
<Button size="small" fill="none" onClick={() => setChooseMonth(true)}>
{monthStr || t('chooseMonth')}
<DownOutline />
</Button>
<Button size="small" fill="none" onClick={() => setChooseType(true)}>
<SearchOutline />
{t(`type.${type}`)}
</Button>
</Space>
<DatePicker visible={chooseMonth} value={month || null} onClose={() => setChooseMonth(false)} max={new Date()} precision="month" onConfirm={(value) => {
setMonth(value);
setChooseMonth(false);
}}/>
<Popup visible={chooseType} onMaskClick={() => setChooseType(false)} onClose={() => setChooseType(false)}>
<Radio.Group onChange={(type) => {
setType(type);
setChooseType(false);
}} value={type}>
<Space className={Styles.types} justify="around">
<Radio value="both">{t('type.both')}</Radio>
<Radio value="in">{t('type.in')}</Radio>
<Radio value="out">{t('type.out')}</Radio>
</Space>
</Radio.Group>
</Popup>
{accountOpers?.length ? <div style={{ flex: 1 }}>
<List>
{accountOpers.map((oper, idx) => {
const { value, time, type, symbol, avail, bgColor } = oper;
return (<List.Item className={Styles.item} key={idx} prefix={<div className={Styles.symbol} style={{
backgroundColor: bgColor,
}}>
{symbol}
</div>} description={<Space direction="horizontal" justify="between" className={Styles.main}>
<span>{time}</span>
<span>{t('account:attr.avail')}:{avail}</span>
</Space>}>
<Space direction="horizontal" justify="between" className={Styles.sub}>
<span className={Styles.type}>{type}</span>
<span style={{
color: bgColor,
}}>{value}</span>
</Space>
</List.Item>);
})}
</List>
<InfiniteScroll hasMore={hasMore} loadMore={loadMore}/>
</div>);
}
return null;
</List>
<InfiniteScroll hasMore={hasMore} loadMore={loadMore}/>
</div> : (<div className={Styles.empty}>
<Result icon={<UndoOutline />} status='success' title={t('common::noData')} description={<Button onClick={() => navigateBack()}>
{t('common::back')}
</Button>}/>
</div>)}
</div>);
}

View File

@ -0,0 +1,27 @@
.control {
padding: 4px;
}
.value {
font-size: x-large;
font-weight: bold;
}
.list {
min-height: 620px;
}
.types {
padding: 20px;
width: 100%;
}
.empty {
display: flex;
width: 100%;
flex: 1;
background-color: white;
align-items: center;
justify-content: center;
}

View File

@ -1,12 +1,24 @@
import React from 'react';
import { WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../oak-app-domain';
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', true, {
accountOpers?: Array<{
value: string;
time: string;
type: string;
symbol: string;
avail: string;
bgColor: string;
}>;
}>): React.JSX.Element | undefined;
hasMore: boolean;
month?: Date;
monthStr: string;
type: 'in' | 'out' | 'both';
chooseMonth: boolean;
chooseType: boolean;
}, {
setMonth: (month?: Date) => void;
setType: (type: 'in' | 'out' | 'both') => void;
setChooseMonth: (cm: boolean) => void;
setChooseType: (ct: boolean) => void;
}>): React.JSX.Element;

View File

@ -1,11 +1,46 @@
import React from 'react';
import { List, Flex } from 'antd';
import { Avatar, List, Select, Flex, Button, Card, Result, DatePicker } from 'antd';
import Styles from './web.module.less';
import dayJs from 'dayjs';
export default function Render(props) {
const { accountOpers } = props.data;
const { t } = props.methods;
if (accountOpers?.length) {
return (<Flex vertical>
<List dataSource={accountOpers}/>
</Flex>);
}
const { accountOpers, hasMore, month, type, chooseMonth, chooseType, monthStr, oakLoading, oakLoadingMore } = props.data;
const { t, loadMore, setType, setMonth, setChooseMonth, setChooseType, navigateBack } = props.methods;
return (<>
<Card title={t('history')} extra={<Flex gap="middle" className={Styles.control}>
<DatePicker picker="month" onChange={(value) => {
setMonth(value.toDate());
}} maxDate={dayJs()}/>
<Select style={{ width: 120 }} onChange={(value) => {
setType(value);
}} value={type} options={[
{ value: 'both', label: t('type.both') },
{ value: 'in', label: t('type.in') },
{ value: 'out', label: t('type.out') },
]}/>
</Flex>}>
{accountOpers?.length ?
<List className={Styles.list} loading={oakLoading || oakLoadingMore} dataSource={accountOpers} renderItem={(item, idx) => (<List.Item key={idx}>
<List.Item.Meta avatar={<Avatar style={{ backgroundColor: item.bgColor }}>{item.symbol}</Avatar>} title={<span className={Styles.type}>{item.type}</span>} description={<Flex justify="between" className={Styles.main} gap="large">
<span>{item.time}</span>
<span>{t('account:attr.avail')}:{item.avail}</span>
</Flex>}/>
<div className={Styles.value} style={{
color: item.bgColor,
}}>
{item.value}
</div>
</List.Item>)} loadMore={!oakLoading && !oakLoadingMore && hasMore ? (<div style={{
textAlign: 'center',
marginTop: 12,
height: 32,
lineHeight: '32px',
}}>
<Button onClick={loadMore}>
{t('common::loadMore')}
</Button>
</div>) : null}/> : (<Result status="404" title={t('common::noData')} extra={<Button onClick={() => navigateBack()}>
{t('common::back')}
</Button>}/>)}
</Card>
</>);
}

View File

@ -264,6 +264,17 @@ export default OakComponent({
});
}
},
updateExternalIdMp(input) {
const { detail } = input;
this.update({
externalId: detail.value,
});
},
clearExternalIdMp() {
this.update({
externalId: null,
});
}
},
lifetimes: {
ready() {

View File

@ -48,6 +48,16 @@
<l-form-item label-width="240rpx" label="{{t('code.label')}}">
<l-tag bg-color="#d9363e">{{pay.phantom3}}</l-tag>
</l-form-item>
<l-form-item label-width="240rpx" label="{{t('externalId.label')}}">
<l-input
hide-label="{{true}}"
clear="{{true}}"
disabled="{{pay.iState !== 'paying'}}"
placeholder="{{t('externalId.help')}}"
bind:lininput="updateExternalIdMp"
bind:linclear="clearExternalIdMp"
/>
</l-form-item>
<block wx:if="{{offline.type === 'bank'}}">
<l-form-item label-width="240rpx" label="{{t('offlineAccount::label.channel.bank')}}">
<view>{{offline.channel}}</view>

View File

@ -25,10 +25,14 @@
},
"code": {
"label": "转帐凭证号",
"help": "请在转帐留言中附上转帐凭证号,便于人工核对"
"help": "请在转帐时于留言中附上转帐凭证号,便于人工核对。若转账时忘记附上,则请准确填写转账流水号。"
},
"channel": {
"change": "更换",
"prefix": "渠道"
},
"externalId": {
"label": "转账流水号",
"help": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}

View File

@ -10,6 +10,7 @@ export declare function RenderOffline(props: {
offlines: ColoredOffline[];
offline: ColoredOffline;
updateOfflineId: (entityId: string) => void;
updateExternalId: (externalId: string) => void;
}): React.JSX.Element;
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
pay?: RowWithActions<EntityDict, 'pay'>;

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Card, Tag, List, Button, Modal, Form, Selector, Popup } from 'antd-mobile';
import { Card, Tag, List, Button, Modal, Form, Selector, Input, Popup } from 'antd-mobile';
import { QRCode, Alert } from 'antd';
import Styles from './web.mobile.module.less';
import * as dayJs from 'dayjs';
@ -9,8 +9,8 @@ import { CentToString } from 'oak-domain/lib/utils/money';
import { PayCircleOutline, GlobalOutline, InformationCircleOutline, CheckCircleOutline } from 'antd-mobile-icons';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
export function RenderOffline(props) {
const { pay, t, offline, offlines, updateOfflineId } = props;
const { meta, iState, phantom3 } = pay;
const { pay, t, offline, offlines, updateOfflineId, updateExternalId } = props;
const { meta, iState, phantom3, externalId } = pay;
const { type, channel, name, qrCode, color } = offline || {};
const [show, setShow] = useState(false);
const items2 = [
@ -23,6 +23,11 @@ export function RenderOffline(props) {
<Tag color="red">
<span style={{ fontSize: 'large' }}>{phantom3}</span>
</Tag>
</Form.Item>,
<Form.Item key="externalId" label={<span className={Styles.bold}>{t('externalId.label')}</span>}>
<Input autoFocus value={externalId || ''} onChange={(value) => {
updateExternalId(value);
}} placeholder={t('externalId.help')} disabled={iState !== 'paying'}/>
</Form.Item>
];
if (type === 'bank') {
@ -121,7 +126,7 @@ function RenderWechatPay(props) {
return null;
}
function RenderPayMeta(props) {
const { pay, notSameApp, t, offlines, offline, updateOfflineId } = props;
const { pay, notSameApp, t, offlines, offline, updateOfflineId, updateExternalId } = props;
const { iState, entity } = pay;
if (entity !== 'offlineAccount' && notSameApp) {
return <Alert type='warning' message={t('notSameApp')}/>;
@ -131,7 +136,7 @@ function RenderPayMeta(props) {
if (offline && offlines) {
return (<>
{iState === 'paying' && <Alert type='info' message={t('code.help')} style={{ marginBottom: 10 }}/>}
<RenderOffline t={t} pay={pay} offline={offline} offlines={offlines} updateOfflineId={updateOfflineId}/>
<RenderOffline t={t} pay={pay} offline={offline} offlines={offlines} updateOfflineId={updateOfflineId} updateExternalId={updateExternalId}/>
</>);
}
return null;
@ -208,6 +213,8 @@ export default function Render(props) {
}
}
]);
}} updateExternalId={(externalId) => {
update({ externalId });
}}/>
</div>
<div className={Styles.padding}/>

View File

@ -10,6 +10,7 @@ export declare function RenderOffline(props: {
offlines: ColoredOffline[];
offline: ColoredOffline;
updateOfflineId: (entityId: string) => void;
updateExternalId: (externalId: string) => void;
}): React.JSX.Element | null;
export default function Render(props: WebComponentProps<EntityDict, 'pay', false, {
pay?: RowWithActions<EntityDict, 'pay'>;

View File

@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react';
import { Tag, Card, QRCode, Form, Descriptions, Typography, Alert, Button, Modal, Radio } from 'antd';
import { Input, Tag, Card, QRCode, Form, Descriptions, Typography, Alert, Button, Modal, Radio } from 'antd';
import { CheckCircleOutlined } from '@ant-design/icons';
import { CentToString } from 'oak-domain/lib/utils/money';
import Styles from './web.pc.module.less';
@ -8,8 +8,8 @@ import duration from 'dayjs/plugin/duration';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
dayJs.extend(duration);
export function RenderOffline(props) {
const { pay, t, offline, offlines, updateOfflineId } = props;
const { iState, phantom3 } = pay;
const { pay, t, offline, offlines, updateOfflineId, updateExternalId } = props;
const { iState, phantom3, externalId } = pay;
const { type, channel, name, qrCode, color } = offline;
const [show, setShow] = useState(false);
const [showQrCode, setShowQrCode] = useState(false);
@ -23,6 +23,12 @@ export function RenderOffline(props) {
<Tag color="red">
<span style={{ fontSize: 'large' }}>{phantom3}</span>
</Tag>
</Form.Item>,
<Form.Item key="externalId" label={<span className={Styles.bold}>{t('externalId.label')}</span>}>
<Input value={externalId || ''} onChange={({ currentTarget }) => {
const { value } = currentTarget;
updateExternalId(value);
}} placeholder={t('externalId.help')} disabled={iState !== 'paying'}/>
</Form.Item>
];
if (type === 'bank') {
@ -123,7 +129,7 @@ function RenderWechatPay(props) {
return null;
}
function RenderPayMeta(props) {
const { pay, notSameApp, t, offlines, offline, updateOfflineId } = props;
const { pay, notSameApp, t, offlines, offline, updateOfflineId, updateExternalId } = props;
const { iState, entity } = pay;
if (entity !== 'offlineAccount' && notSameApp) {
return <Alert type='warning' message={t('notSameApp')}/>;
@ -133,7 +139,7 @@ function RenderPayMeta(props) {
if (offline && offlines) {
return (<>
{iState === 'paying' && <Alert type='info' message={t('code.help')} style={{ marginBottom: 10 }}/>}
<RenderOffline t={t} pay={pay} offline={offline} offlines={offlines} updateOfflineId={updateOfflineId}/>
<RenderOffline t={t} pay={pay} offline={offline} offlines={offlines} updateOfflineId={updateOfflineId} updateExternalId={updateExternalId}/>
</>);
}
return null;
@ -214,6 +220,10 @@ export default function Render(props) {
}
}
]);
}} updateExternalId={(externalId) => {
update({
externalId,
});
}}/>
</div>
<div className={Styles.btn}>

View File

@ -146,6 +146,8 @@ export default function Render(props) {
{offlines && <RenderOffline pay={spRow} t={t} offlines={offlines} offline={offlines.find(ele => ele.id === spRow.entityId)} updateOfflineId={(entityId) => updateItem({
entity: 'offlineAccount',
entityId
}, spId)} updateExternalId={(externalId) => updateItem({
externalId,
}, spId)}/>}
<Divider style={{ width: '80%', alignSelf: 'flex-end' }}/>
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 12 }} layout="horizontal" style={{ width: '100%', marginTop: 12 }}>

View File

@ -5,5 +5,6 @@ declare const _default: (props: import("oak-frontend-base").ReactComponentProps<
onNewWithdrawAccount: () => void;
onCreateWithdraw: (id: string) => void;
onGoToHistory: () => void;
onGoToWaManage: () => void;
}>) => React.ReactElement;
export default _default;

View File

@ -10,6 +10,7 @@ export default OakComponent({
onNewWithdrawAccount: () => undefined,
onCreateWithdraw: (id) => undefined,
onGoToHistory: () => undefined,
onGoToWaManage: () => undefined,
},
formData({ features }) {
const { accountId, withdrawAccountFilter } = this.props;
@ -220,6 +221,9 @@ export default OakComponent({
onGoToHistoryMp() {
const { onGoToHistory } = this.props;
onGoToHistory && onGoToHistory();
},
onGoToWaManageMp() {
this.props.onGoToWaManage();
}
},
});

View File

@ -105,6 +105,10 @@
<oak-icon name="coupons" size="18" color="@oak-color-primary" style="margin-right:8rpx;"/>
<span>{{t('gotoHistory')}}</span>
</view>
<view class="gotoHistory" bindtap="onGoToWaManageMp">
<oak-icon name="enterinto" size="18" color="@oak-color-primary" style="margin-right:8rpx;"/>
<span>{{t('gotoWaManage')}}</span>
</view>
<view class="footer">
<l-button
size="long"

View File

@ -30,5 +30,6 @@
"overflowManualAmount": "提现额度不能超过人工划款的额度"
},
"innerLogic": "自动运算",
"gotoHistory": "提现历史"
"gotoHistory": "提现历史",
"gotoWaManage": "提现账户管理"
}

View File

@ -21,6 +21,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
waFilter?: EntityDict['withdrawAccount']['Selection']['filter'];
userId: string;
onGoToHistory: () => void;
onGoToWaManage: () => void;
}, {
setValue: (v: string | number | null) => void;
switchHelp: (type: 'method' | 'loss') => void;

View File

@ -1,12 +1,12 @@
import React from 'react';
import { Button, Input, Result } from 'antd-mobile';
import { DownOutline, UpOutline, ReceiptOutline } from 'antd-mobile-icons';
import { DownOutline, UpOutline, ReceiptOutline, BankcardOutline } from 'antd-mobile-icons';
import Styles from './web.module.less';
import classNames from 'classnames';
import WithDrawAccountPicker from '../../withdrawAccount/list';
import WithdrawDisplay from '../display';
export default function render(props) {
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan, manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale, withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter, userId, onGoToHistory } = props.data;
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan, manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale, withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter, userId, onGoToHistory, onGoToWaManage } = props.data;
const { t, setValue, switchHelp, createWithdrawData, restartAll, createWithdraw, goBack, pickWithdrawChannel } = props.methods;
if (!withdrawable) {
return (<Result status='error' title={t('notSetYet')} description={<Button color="primary" onClick={() => goBack()}>
@ -85,6 +85,10 @@ export default function render(props) {
<ReceiptOutline scale={1.4}/>
{t('gotoHistory')}
</div>
<div className={Styles.gotoHistory} onClick={onGoToWaManage}>
<BankcardOutline scale={1.4}/>
{t('gotoWaManage')}
</div>
<div style={{ flex: 1 }}/>
<Button className={Styles.btn} block color="primary" disabled={!executale} onClick={() => createWithdrawData()}>
{t('common::confirm')}

View File

@ -21,6 +21,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
waFilter?: EntityDict['withdrawAccount']['Selection']['filter'];
userId: string;
onGoToHistory: () => void;
onGoToWaManage: () => void;
}, {
setValue: (v: number | null) => void;
switchHelp: (type: 'method' | 'loss') => void;

View File

@ -6,7 +6,7 @@ import classNames from 'classnames';
import WithDrawAccountPicker from '../../withdrawAccount/list';
import WithdrawDisplay from '../display';
export default function render(props) {
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan, manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale, withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter, userId, onGoToHistory } = props.data;
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan, manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale, withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter, userId, onGoToHistory, onGoToWaManage } = props.data;
const { t, setValue, switchHelp, createWithdrawData, restartAll, createWithdraw, goBack, pickWithdrawChannel } = props.methods;
if (!withdrawable) {
return (<Result status="500" title="500" subTitle={t('notSetYet')} extra={<Button type="primary" onClick={() => goBack()}>
@ -70,6 +70,9 @@ export default function render(props) {
<div className={Styles.gotoHistory} onClick={onGoToHistory}>
{t('gotoHistory')}
</div>
<div className={Styles.gotoHistory} onClick={onGoToWaManage}>
{t('gotoWaManage')}
</div>
<div style={{ flex: 1 }}/>
<Button className={Styles.btn} size="large" type="primary" disabled={!executale} onClick={() => createWithdrawData()}>
{t('common::confirm')}

View File

@ -47,7 +47,9 @@
}
.refundItem {
width: 223px;
// width: 223px;
width: 100%;
box-sizing: border-box;
margin: 12px;
padding: 8px;
font-size: small;
@ -77,7 +79,7 @@
margin-right: 4px;
}
}
.reason {
color: var(--oak-color-error);
}

View File

@ -67,7 +67,12 @@ const attrUpdateMatrix = {
}
},
externalId: {
actions: ['startPaying', 'succeedPaying'],
actions: ['startPaying', 'succeedPaying', 'update'],
filter: {
iState: {
$in: ['unpaid', 'paying'],
},
},
},
},
refund: {

View File

@ -52,7 +52,15 @@ const i18ns = [
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/accountOper/list",
data: {}
data: {
"history": "账户历史",
"chooseMonth": "选择月份",
"type": {
"both": "全部",
"in": "仅收入",
"out": "仅支出"
}
}
},
{
id: "65d19e4723a89c25e3c10a87013af430",
@ -198,11 +206,15 @@ const i18ns = [
},
"code": {
"label": "转帐凭证号",
"help": "请在转帐留言中附上转帐凭证号,便于人工核对"
"help": "请在转帐时于留言中附上转帐凭证号,便于人工核对。若转账时忘记附上,则请准确填写转账流水号。"
},
"channel": {
"change": "更换",
"prefix": "渠道"
},
"externalId": {
"label": "转账流水号",
"help": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -449,7 +461,8 @@ const i18ns = [
"overflowManualAmount": "提现额度不能超过人工划款的额度"
},
"innerLogic": "自动运算",
"gotoHistory": "提现历史"
"gotoHistory": "提现历史",
"gotoWaManage": "提现账户管理"
}
},
{
@ -696,6 +709,7 @@ const i18ns = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

@ -1,4 +1,4 @@
import { String, Price, Boolean, Datetime } from 'oak-domain/lib/types/DataType';
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 'oak-general-business/lib/entities/User';
@ -9,7 +9,7 @@ export interface Schema extends EntityShape {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creator: User;
entity: String<32>;

View File

@ -46,6 +46,7 @@
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

@ -1 +1 @@
{ "name": "帐号操作", "attr": { "account": "帐号", "type": "类型", "totalPlus": "余额变化", "availPlus": "可用余额变化", "refundablePlus": "可退款余额变化", "total": "操作后余额", "avail": "操作后可用余额", "refundable": "操作后可退款余额", "entity": "关联对象", "entityId": "关联对象Id" }, "v": { "type": { "deposit": "充值", "withdraw": "提现", "consume": "消费", "loan": "抵押", "repay": "偿还", "withdrawBack": "提现失败", "earn": "赚取", "encash": "兑现", "tax": "渠道费", "taxRefund": "渠道费用退还", "cutoffRefundable": "削减可自由退额度", "refund": "退款", "refundFailure": "退款失败" } } }
{ "name": "帐号操作", "attr": { "account": "帐号", "type": "类型", "totalPlus": "余额变化", "availPlus": "可用余额变化", "refundablePlus": "可退款余额变化", "total": "操作后余额", "avail": "操作后可用余额", "refundable": "操作后可退款余额", "entity": "关联对象", "entityId": "关联对象Id" }, "v": { "type": { "deposit": "充值", "withdraw": "提现", "consume": "消费", "loan": "抵押", "repay": "偿还", "withdrawBack": "提现未遂", "earn": "赚取", "encash": "兑现", "tax": "渠道费", "taxRefund": "渠道费用退还", "cutoffRefundable": "削减可自由退额度", "refund": "退款", "refundFailure": "退款失败" } } }

View File

@ -3,7 +3,7 @@ import { Q_DateValue, Q_BooleanValue, Q_NumberValue, Q_StringValue, Q_EnumValue,
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import { Price, String, Datetime, Boolean } from "oak-domain/lib/types/DataType";
import { Price, String, Text, Datetime, Boolean } from "oak-domain/lib/types/DataType";
import * as User from "../User/Schema";
import * as System from "../System/Schema";
import * as Pay from "../Pay/Schema";
@ -13,7 +13,7 @@ export type OpSchema = EntityShape & {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creatorId: ForeignKey<"user">;
entity: String<32>;
@ -28,7 +28,7 @@ export type Schema = EntityShape & {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creatorId: ForeignKey<"user">;
entity: String<32>;

View File

@ -22,10 +22,7 @@ export const desc = {
},
desc: {
notNull: true,
type: "varchar",
params: {
length: 64
}
type: "text"
},
timeoutAt: {
notNull: true,

View File

@ -3,7 +3,7 @@ export declare function getWithdrawCreateData(params: {
accountId: string;
price: number;
withdrawAccountId?: string;
}, context: BRC): Promise<(Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}, context: BRC): Promise<(Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
accountId: string;
@ -17,7 +17,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
accountId: string;
@ -31,7 +31,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
account?: undefined;
@ -45,7 +45,7 @@ export declare function getWithdrawCreateData(params: {
modiEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/ModiEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
operEntity$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/OperEntity/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
accountOper$entity?: import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">[]> | import("oak-domain/lib/types").Operation<"create", Omit<import("../oak-app-domain/AccountOper/Schema").CreateOperationData, "entity" | "entityId">>[] | undefined;
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "accountId" | "creatorId">> & {
}) | (Partial<Omit<import("../oak-app-domain/Withdraw/Schema").OpSchema, "creatorId" | "accountId">> & {
id: string;
} & {
account?: undefined;

View File

@ -269,6 +269,17 @@ exports.default = OakComponent({
});
}
},
updateExternalIdMp(input) {
const { detail } = input;
this.update({
externalId: detail.value,
});
},
clearExternalIdMp() {
this.update({
externalId: null,
});
}
},
lifetimes: {
ready() {

View File

@ -69,7 +69,12 @@ const attrUpdateMatrix = {
}
},
externalId: {
actions: ['startPaying', 'succeedPaying'],
actions: ['startPaying', 'succeedPaying', 'update'],
filter: {
iState: {
$in: ['unpaid', 'paying'],
},
},
},
},
refund: {

View File

@ -15,7 +15,7 @@ export declare class BackendRuntimeContext<ED extends EntityDict & BaseEntityDic
type?: number | undefined;
systemId?: number | undefined;
system?: import("../oak-app-domain/System/Schema").Projection | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
style?: number | import("oak-domain/lib/types").JsonProjection<import("oak-general-business/lib/types/Style").Style> | undefined;
domainId?: number | undefined;
domain?: import("../oak-app-domain/Domain/Schema").Projection | undefined;

View File

@ -54,7 +54,15 @@ const i18ns = [
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/accountOper/list",
data: {}
data: {
"history": "账户历史",
"chooseMonth": "选择月份",
"type": {
"both": "全部",
"in": "仅收入",
"out": "仅支出"
}
}
},
{
id: "65d19e4723a89c25e3c10a87013af430",
@ -200,11 +208,15 @@ const i18ns = [
},
"code": {
"label": "转帐凭证号",
"help": "请在转帐留言中附上转帐凭证号,便于人工核对"
"help": "请在转帐时于留言中附上转帐凭证号,便于人工核对。若转账时忘记附上,则请准确填写转账流水号。"
},
"channel": {
"change": "更换",
"prefix": "渠道"
},
"externalId": {
"label": "转账流水号",
"help": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -451,7 +463,8 @@ const i18ns = [
"overflowManualAmount": "提现额度不能超过人工划款的额度"
},
"innerLogic": "自动运算",
"gotoHistory": "提现历史"
"gotoHistory": "提现历史",
"gotoWaManage": "提现账户管理"
}
},
{
@ -698,6 +711,7 @@ const i18ns = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

@ -1,4 +1,4 @@
import { String, Price, Boolean, Datetime } from 'oak-domain/lib/types/DataType';
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 'oak-general-business/lib/entities/User';
@ -9,7 +9,7 @@ export interface Schema extends EntityShape {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creator: User;
entity: String<32>;

View File

@ -46,6 +46,7 @@
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

@ -1 +1 @@
{ "name": "帐号操作", "attr": { "account": "帐号", "type": "类型", "totalPlus": "余额变化", "availPlus": "可用余额变化", "refundablePlus": "可退款余额变化", "total": "操作后余额", "avail": "操作后可用余额", "refundable": "操作后可退款余额", "entity": "关联对象", "entityId": "关联对象Id" }, "v": { "type": { "deposit": "充值", "withdraw": "提现", "consume": "消费", "loan": "抵押", "repay": "偿还", "withdrawBack": "提现失败", "earn": "赚取", "encash": "兑现", "tax": "渠道费", "taxRefund": "渠道费用退还", "cutoffRefundable": "削减可自由退额度", "refund": "退款", "refundFailure": "退款失败" } } }
{ "name": "帐号操作", "attr": { "account": "帐号", "type": "类型", "totalPlus": "余额变化", "availPlus": "可用余额变化", "refundablePlus": "可退款余额变化", "total": "操作后余额", "avail": "操作后可用余额", "refundable": "操作后可退款余额", "entity": "关联对象", "entityId": "关联对象Id" }, "v": { "type": { "deposit": "充值", "withdraw": "提现", "consume": "消费", "loan": "抵押", "repay": "偿还", "withdrawBack": "提现未遂", "earn": "赚取", "encash": "兑现", "tax": "渠道费", "taxRefund": "渠道费用退还", "cutoffRefundable": "削减可自由退额度", "refund": "退款", "refundFailure": "退款失败" } } }

View File

@ -3,7 +3,7 @@ import { Q_DateValue, Q_BooleanValue, Q_NumberValue, Q_StringValue, Q_EnumValue,
import { OneOf } from "oak-domain/lib/types/Polyfill";
import { FormCreateData, FormUpdateData, DeduceAggregation, Operation as OakOperation, Selection as OakSelection, MakeAction as OakMakeAction, AggregationResult, EntityShape } from "oak-domain/lib/types/Entity";
import { Action, ParticularAction, IState } from "./Action";
import { Price, String, Datetime, Boolean } from "oak-domain/lib/types/DataType";
import { Price, String, Text, Datetime, Boolean } from "oak-domain/lib/types/DataType";
import * as User from "../User/Schema";
import * as System from "../System/Schema";
import * as Pay from "../Pay/Schema";
@ -13,7 +13,7 @@ export type OpSchema = EntityShape & {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creatorId: ForeignKey<"user">;
entity: String<32>;
@ -28,7 +28,7 @@ export type Schema = EntityShape & {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creatorId: ForeignKey<"user">;
entity: String<32>;

View File

@ -25,10 +25,7 @@ exports.desc = {
},
desc: {
notNull: true,
type: "varchar",
params: {
length: 64
}
type: "text"
},
timeoutAt: {
notNull: true,

View File

@ -14,7 +14,7 @@ export declare const mergedProjection: {
type?: number | undefined;
systemId?: number | undefined;
system?: import("../oak-app-domain/System/Schema").Projection | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
config?: number | import("oak-domain/lib/types").JsonProjection<import("../oak-app-domain/Application/Schema").WechatMpConfig | import("../oak-app-domain/Application/Schema").WebConfig | import("../oak-app-domain/Application/Schema").WechatPublicConfig | import("../oak-app-domain/Application/Schema").NativeConfig> | undefined;
style?: number | import("oak-domain/lib/types").JsonProjection<import("oak-general-business/lib/types/Style").Style> | undefined;
domainId?: number | undefined;
domain?: import("../oak-app-domain/Domain/Schema").Projection | undefined;

View File

@ -1,6 +1,6 @@
{
"name": "oak-pay-business",
"version": "2.2.0",
"version": "2.2.1",
"description": "",
"files": [
"lib/**/*",
@ -51,7 +51,7 @@
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"oak-domain": "~5.0.11",
"oak-frontend-base": "~5.2.1",
"oak-frontend-base": "~5.2.2",
"oak-general-business": "~5.2.3",
"react": "~18.2.0",
"react-dom": "~18.2.0",

View File

@ -1 +1,10 @@
{}
{
"usingComponents": {
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index",
"l-popup": "@oak-frontend-base/miniprogram_npm/lin-ui/popup/index",
"l-list": "@oak-frontend-base/miniprogram_npm/lin-ui/list/index",
"l-radio-group": "@oak-frontend-base/miniprogram_npm/lin-ui/radio-group/index",
"l-radio": "@oak-frontend-base/miniprogram_npm/lin-ui/radio/index",
"oak-icon": "@oak-frontend-base/components/icon/index"
}
}

View File

@ -0,0 +1,125 @@
@import '../../../config/styles/mp/index.less';
@import '../../../config/styles/mp/mixins.less';
.myScroll {
height: 100%;
display: flex;
flex-direction: column;
}
.header {
position: sticky;
top: 0rpx;
left: 0rpx;
width: 100%;
z-index: 10;
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
box-sizing: border-box;
background-color: @oak-bg-color-page;
color: @oak-text-color-primary;
}
.headerItem {
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.list {
flex: 1;
background-color: #fff;
}
.my-list-class {
border-bottom: 2rpx solid @oak-border-color;
}
.left {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 24rpx 0rpx;
}
.right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 16rpx;
padding: 24rpx 0rpx;
margin-left: 12rpx;
}
.avatar {
font-size: 40rpx;
font-weight: bold;
width: 96rpx;
height: 96rpx;
border-radius: 48rpx;
display: flex;
align-items: center;
justify-content: center;
color: @oak-text-color-primary;
}
.middle {
margin-left: 20rpx;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 16rpx;
}
.type {
font-weight: bold;
font-size: 32rpx;
}
.sub {
color: #888;
font-size: 26rpx;
}
.empty {
height: calc(100% - 90rpx);
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
gap: 24rpx;
}
.icon {
color: @oak-color-primary;
}
.btn {
color: #333;
border: 2rpx solid #eee;
}
.container {
background-color: #fff;
height: 15vh;
padding: 32rpx;
box-sizing: border-box;
}
.radio-group-class {
width: 100%;
display: grid;
grid-column: 3;
grid-template-columns: 1fr 1fr 1fr;
}

View File

@ -21,6 +21,8 @@ export default OakComponent({
type: 'both' as 'in' | 'out' | 'both',
chooseMonth: false,
chooseType: false,
pickerStart: dayjs().subtract(10, 'y').startOf('m').format('YYYY-MM-DD'),
pickerEnd: dayjs().startOf('m').format('YYYY-MM-DD'),
},
filters: [
{
@ -145,6 +147,28 @@ export default OakComponent({
},
setChooseType(chooseType: boolean) {
this.setState({ chooseType });
},
setMonthMp(e: { detail: { value: string } }) {
const { value } = e.detail;
const month = dayjs(value).valueOf();
this.setMonth(month);
},
openChooseTypeMp() {
this.setState({
chooseType: true,
})
},
closeChooseTypeMp() {
this.setState({
chooseType: false,
})
},
setTypeMp({ detail }: any) {
const { key, } = detail;
this.setState({
type: key,
}, () => this.setTypeFilter())
this.closeChooseTypeMp();
}
}
})

View File

@ -0,0 +1,56 @@
<scroll-view scroll-y bindscrolltolower="loadMoreMp" class="myScroll" refresher-enabled="{{true}}"
bindrefresherrefresh="bindrefresherrefresh" refresher-triggered="{{refreshing}}">
<view class="header">
<picker mode="date" fields="month" value="{{monthStr}}" start="{{pickerStart}}" end="{{pickerEnd}}" bindchange="setMonthMp">
<view class="headerItem">
<span>{{monthStr || t('chooseMonth')}}</span>
<oak-icon name="unfold" size="20" color="#000"/>
</view>
</picker>
<view class="headerItem" bindtap="openChooseTypeMp">
<oak-icon name="search" size="20" color="#000" />
<span>{{t('type.'+type)}}</span>
</view>
</view>
<view wx:if="{{accountOpers && accountOpers.length>0}}" class="list">
<l-list wx:for="{{accountOpers}}" wx:key="id" gap="24" is-link="{{false}}" l-class="my-list-class">
<view slot="left-section" class="left">
<view class="avatar" style="background-color:{{item.bgColor}}">{{item.symbol}}</view>
<view class="middle">
<view class="type">{{item.type}}</view>
<view class="sub">{{item.time}}</view>
</view>
</view>
<view slot="right-section" class="right">
<view style="color:{{item.bgColor}}">{{item.value}}</view>
<view class="sub">{{t('account:attr.avail')}}{{item.avail}}</view>
</view>
</l-list>
</view>
<view wx:else class="empty">
<view class="content">
<oak-icon name="refresh" size="48" class="icon"/>
<view>{{t('common::noData')}}</view>
<l-button
plain="true"
bind:lintap="navigateBack"
l-class="btn"
size="long"
width="160"
height="80"
>
{{t('common::back')}}
</l-button>
</view>
</view>
</scroll-view>
<l-popup show="{{chooseType}}" content-align="bottom" bind:lintap="closeChooseTypeMp" >
<view class="container">
<l-radio-group current="{{type}}" bind:linchange="setTypeMp" none-checked="{{false}}" placement="row" l-class="radio-group-class">
<l-radio key="both">{{t('type.both')}}</l-radio>
<l-radio key="in">{{t('type.in')}}</l-radio>
<l-radio key="out">{{t('type.out')}}</l-radio>
</l-radio-group>
</view>
</view>
</l-popup>

View File

@ -1,4 +1,5 @@
{
"history": "账户历史",
"chooseMonth": "选择月份",
"type": {
"both": "全部",

View File

@ -0,0 +1,27 @@
.control {
padding: 4px;
}
.value {
font-size: x-large;
font-weight: bold;
}
.list {
min-height: 620px;
}
.types {
padding: 20px;
width: 100%;
}
.empty {
display: flex;
width: 100%;
flex: 1;
background-color: white;
align-items: center;
justify-content: center;
}

View File

@ -1,12 +1,12 @@
import React, { useState } from 'react';
import { Input, Radio, Space, List, Select, Flex, Button } from 'antd';
import { Radio, Avatar, List, Select, Flex, Button, Card, Result, DatePicker, Modal } from 'antd';
import { CaretDownFilled, SearchOutlined } from '@ant-design/icons';
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
import { EntityDict } from '../../../oak-app-domain';
import Styles from './web.pc.module.less';
import classNames from 'classnames';
import Styles from './web.module.less';
import dayJs from 'dayjs';
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', false, {
export default function Render(props: WebComponentProps<EntityDict, 'accountOper', true, {
accountOpers?: Array<{
value: string;
time: string;
@ -22,39 +22,109 @@ export default function Render(props: WebComponentProps<EntityDict, 'accountOper
chooseMonth: boolean;
chooseType: boolean;
}, {
setMonth: (month?: number | Date) => void;
setMonth: (month?: Date) => void;
setType: (type: 'in' | 'out' | 'both') => void;
setChooseMonth: (cm: boolean) => void;
setChooseType: (ct: boolean) => void;
}>) {
const { accountOpers, hasMore, month, type, chooseMonth, chooseType, monthStr } = props.data;
const { accountOpers, hasMore, month, type, chooseMonth, chooseType, monthStr, oakLoading, oakLoadingMore } = props.data;
const { t, loadMore, setType, setMonth, setChooseMonth, setChooseType, navigateBack } = props.methods;
if (accountOpers?.length) {
return (
<Flex
vertical
return (
<>
<Card
title={t('history')}
extra={
<Flex
gap="middle"
className={Styles.control}
>
<DatePicker
picker="month"
onChange={(value) => {
setMonth(value.toDate());
}}
maxDate={dayJs()}
/>
<Select
style={{ width: 120 }}
onChange={(value) => {
setType(value as 'in');
}}
value={type}
options={[
{ value: 'both', label: t('type.both') },
{ value: 'in', label: t('type.in') },
{ value: 'out', label: t('type.out') },
]}
/>
</Flex>
}
>
<Flex>
<Button
size="small"
onClick={() => setChooseMonth(true)}
>
{monthStr || t('chooseMonth')}
<CaretDownFilled />
</Button>
<Button
size="small"
onClick={() => setChooseType(true)}
>
<SearchOutlined />
{t(`type.${type}`)}
</Button>
</Flex>
<List
dataSource={accountOpers}
/>
</Flex>
);
}
{
accountOpers?.length ?
<List
className={Styles.list}
loading={oakLoading || oakLoadingMore}
dataSource={accountOpers}
renderItem={(item, idx) => (
<List.Item
key={idx}
>
<List.Item.Meta
avatar={<Avatar style={{ backgroundColor: item.bgColor }}>{item.symbol}</Avatar>}
title={<span className={Styles.type}>{item.type}</span>}
description={
<Flex
justify="between"
className={Styles.main}
gap="large"
>
<span>{item.time}</span>
<span>{t('account:attr.avail')}:{item.avail}</span>
</Flex>
}
/>
<div
className={Styles.value}
style={{
color: item.bgColor,
}}
>
{item.value}
</div>
</List.Item>
)}
loadMore={
!oakLoading && !oakLoadingMore && hasMore ? (
<div
style={{
textAlign: 'center',
marginTop: 12,
height: 32,
lineHeight: '32px',
}}
>
<Button onClick={loadMore}>
{t('common::loadMore')}
</Button>
</div>
) : null
}
/> : (
<Result
status="404"
title={t('common::noData')}
extra={
<Button
onClick={() => navigateBack()}
>
{t('common::back')}
</Button>}
/>
)
}
</Card>
</>
);
}

View File

@ -74,6 +74,7 @@ export default function Render(props: WebComponentProps<EntityDict, 'accountOper
setType(type as 'in');
setChooseType(false);
}}
value={type}
>
<Space
className={Styles.types}

View File

@ -290,6 +290,17 @@ export default OakComponent({
})
}
},
updateExternalIdMp(input: WechatMiniprogram.Input) {
const { detail } = input;
this.update({
externalId: detail.value,
});
},
clearExternalIdMp() {
this.update({
externalId: null,
});
}
},
lifetimes: {
ready() {

View File

@ -48,6 +48,16 @@
<l-form-item label-width="240rpx" label="{{t('code.label')}}">
<l-tag bg-color="#d9363e">{{pay.phantom3}}</l-tag>
</l-form-item>
<l-form-item label-width="240rpx" label="{{t('externalId.label')}}">
<l-input
hide-label="{{true}}"
clear="{{true}}"
disabled="{{pay.iState !== 'paying'}}"
placeholder="{{t('externalId.help')}}"
bind:lininput="updateExternalIdMp"
bind:linclear="clearExternalIdMp"
/>
</l-form-item>
<block wx:if="{{offline.type === 'bank'}}">
<l-form-item label-width="240rpx" label="{{t('offlineAccount::label.channel.bank')}}">
<view>{{offline.channel}}</view>

View File

@ -25,10 +25,14 @@
},
"code": {
"label": "转帐凭证号",
"help": "请在转帐留言中附上转帐凭证号,便于人工核对"
"help": "请在转帐时于留言中附上转帐凭证号,便于人工核对。若转账时忘记附上,则请准确填写转账流水号。"
},
"channel": {
"change": "更换",
"prefix": "渠道"
},
"externalId": {
"label": "转账流水号",
"help": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}

View File

@ -16,10 +16,11 @@ export function RenderOffline(props: {
t: (key: string) => string,
offlines: ColoredOffline[];
offline: ColoredOffline;
updateOfflineId: (entityId: string) => void
updateOfflineId: (entityId: string) => void;
updateExternalId: (externalId: string) => void;
}) {
const { pay, t, offline, offlines, updateOfflineId } = props;
const { iState, phantom3 } = pay;
const { pay, t, offline, offlines, updateOfflineId, updateExternalId } = props;
const { iState, phantom3, externalId } = pay;
const { type, channel, name, qrCode, color } = offline;
const [show, setShow] = useState(false);
@ -41,6 +42,20 @@ export function RenderOffline(props: {
<Tag color="red">
<span style={{ fontSize: 'large' }}>{phantom3}</span>
</Tag>
</Form.Item>,
<Form.Item
key="externalId"
label={<span className={Styles.bold}>{t('externalId.label')}</span>}
>
<Input
value={externalId || ''}
onChange={({ currentTarget }) => {
const { value } = currentTarget;
updateExternalId(value);
}}
placeholder={t('externalId.help')}
disabled={iState !== 'paying'}
/>
</Form.Item>
];
if (type === 'bank') {
@ -228,8 +243,9 @@ function RenderPayMeta(props: {
offlines?: ColoredOffline[];
offline?: ColoredOffline;
updateOfflineId: (entityId: string) => void
updateExternalId: (externalId: string) => void;
}) {
const { pay, notSameApp, t, offlines, offline, updateOfflineId } = props;
const { pay, notSameApp, t, offlines, offline, updateOfflineId, updateExternalId } = props;
const { iState, entity } = pay;
if (entity !== 'offlineAccount' && notSameApp) {
return <Alert type='warning' message={t('notSameApp')} />
@ -246,6 +262,7 @@ function RenderPayMeta(props: {
offline={offline}
offlines={offlines}
updateOfflineId={updateOfflineId}
updateExternalId={updateExternalId}
/>
</>
);
@ -384,6 +401,11 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
}
])
}}
updateExternalId={(externalId) => {
update({
externalId,
});
}}
/>
</div>
<div className={Styles.btn}>

View File

@ -21,10 +21,11 @@ export function RenderOffline(props: {
t: (key: string) => string,
offlines: ColoredOffline[];
offline: ColoredOffline;
updateOfflineId: (entityId: string) => void
updateOfflineId: (entityId: string) => void;
updateExternalId: (externalId: string) => void;
}) {
const { pay, t, offline, offlines, updateOfflineId } = props;
const { meta, iState, phantom3 } = pay;
const { pay, t, offline, offlines, updateOfflineId, updateExternalId } = props;
const { meta, iState, phantom3, externalId } = pay;
const { type, channel, name, qrCode, color } = offline || {};
const [show, setShow] = useState(false);
@ -45,6 +46,20 @@ export function RenderOffline(props: {
<Tag color="red">
<span style={{ fontSize: 'large' }}>{phantom3}</span>
</Tag>
</Form.Item>,
<Form.Item
key="externalId"
label={<span className={Styles.bold}>{t('externalId.label')}</span>}
>
<Input
autoFocus
value={externalId || ''}
onChange={(value) => {
updateExternalId(value);
}}
placeholder={t('externalId.help')}
disabled={iState !== 'paying'}
/>
</Form.Item>
];
if (type === 'bank') {
@ -221,9 +236,10 @@ function RenderPayMeta(props: {
t: (key: string) => string,
offlines?: ColoredOffline[];
offline?: ColoredOffline;
updateOfflineId: (entityId: string) => void
updateOfflineId: (entityId: string) => void;
updateExternalId: (externalId: string) => void;
}) {
const { pay, notSameApp, t, offlines, offline, updateOfflineId } = props;
const { pay, notSameApp, t, offlines, offline, updateOfflineId, updateExternalId } = props;
const { iState, entity } = pay;
if (entity !== 'offlineAccount' && notSameApp) {
return <Alert type='warning' message={t('notSameApp')} />
@ -240,6 +256,7 @@ function RenderPayMeta(props: {
offline={offline}
offlines={offlines}
updateOfflineId={updateOfflineId}
updateExternalId={updateExternalId}
/>
</>
);
@ -383,6 +400,9 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
}
])
}}
updateExternalId={(externalId) => {
update({ externalId });
}}
/>
</div>
<div className={Styles.padding} />

View File

@ -197,6 +197,9 @@ export default function Render(props: WebComponentProps<EntityDict, 'pay', false
entity: 'offlineAccount',
entityId
}, spId)}
updateExternalId={(externalId) => updateItem({
externalId,
}, spId)}
/>}
<Divider style={{ width: '80%', alignSelf: 'flex-end' }} />
<Form

View File

@ -12,6 +12,7 @@ export default OakComponent({
onNewWithdrawAccount: () => undefined as void,
onCreateWithdraw: (id: string) => undefined as void,
onGoToHistory: () => undefined as void,
onGoToWaManage: () => undefined as void,
},
formData({ features }) {
const { accountId, withdrawAccountFilter } = this.props;
@ -224,6 +225,9 @@ export default OakComponent({
onGoToHistoryMp() {
const { onGoToHistory } = this.props;
onGoToHistory && onGoToHistory();
},
onGoToWaManageMp() {
this.props.onGoToWaManage!();
}
},
})

View File

@ -105,6 +105,10 @@
<oak-icon name="coupons" size="18" color="@oak-color-primary" style="margin-right:8rpx;"/>
<span>{{t('gotoHistory')}}</span>
</view>
<view class="gotoHistory" bindtap="onGoToWaManageMp">
<oak-icon name="enterinto" size="18" color="@oak-color-primary" style="margin-right:8rpx;"/>
<span>{{t('gotoWaManage')}}</span>
</view>
<view class="footer">
<l-button
size="long"

View File

@ -30,5 +30,6 @@
"overflowManualAmount": "提现额度不能超过人工划款的额度"
},
"innerLogic": "自动运算",
"gotoHistory": "提现历史"
"gotoHistory": "提现历史",
"gotoWaManage": "提现账户管理"
}

View File

@ -28,6 +28,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
waFilter?: EntityDict['withdrawAccount']['Selection']['filter'];
userId: string;
onGoToHistory: () => void;
onGoToWaManage: () => void;
}, {
setValue: (v: number | null) => void;
switchHelp: (type: 'method' | 'loss') => void;
@ -40,7 +41,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan,
manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale,
withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter,
userId, onGoToHistory
userId, onGoToHistory, onGoToWaManage
} = props.data;
const { t, setValue, switchHelp, createWithdrawData,
restartAll, createWithdraw, goBack, pickWithdrawChannel } = props.methods;
@ -177,6 +178,12 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
>
{t('gotoHistory')}
</div>
<div
className={Styles.gotoHistory}
onClick={onGoToWaManage}
>
{t('gotoWaManage')}
</div>
<div style={{ flex: 1 }} />
<Button
className={Styles.btn}

View File

@ -2,7 +2,7 @@ import React from 'react';
import { EntityDict } from "../../../oak-app-domain";
import { RowWithActions, WebComponentProps } from "oak-frontend-base";
import { Button, Input, Result } from 'antd-mobile';
import { DownOutline, UpOutline, ReceiptOutline } from 'antd-mobile-icons';
import { DownOutline, UpOutline, ReceiptOutline, BankcardOutline } from 'antd-mobile-icons';
import Styles from './web.module.less';
import classNames from 'classnames';
import WithDrawAccountPicker from '../../withdrawAccount/list';
@ -28,6 +28,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
waFilter?: EntityDict['withdrawAccount']['Selection']['filter'];
userId: string;
onGoToHistory: () => void;
onGoToWaManage: () => void;
}, {
setValue: (v: string | number | null) => void;
switchHelp: (type: 'method' | 'loss') => void;
@ -40,7 +41,7 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
const { account, value, refundAmount, manualAmount, avail, availYuan, valueYuan,
manualAmountYuan, refundAmountYuan, showMethodHelp, showLossHelp, executale,
withdrawCreate, withdrawable, withdrawLossText, chooseWa, waFilter,
userId, onGoToHistory
userId, onGoToHistory, onGoToWaManage
} = props.data;
const { t, setValue, switchHelp, createWithdrawData,
restartAll, createWithdraw, goBack, pickWithdrawChannel } = props.methods;
@ -195,6 +196,13 @@ export default function render(props: WebComponentProps<EntityDict, 'withdraw',
<ReceiptOutline scale={1.4} />
{t('gotoHistory')}
</div>
<div
className={Styles.gotoHistory}
onClick={onGoToWaManage}
>
<BankcardOutline scale={1.4} />
{t('gotoWaManage')}
</div>
<div style={{ flex: 1 }} />
<Button
className={Styles.btn}

View File

@ -47,7 +47,9 @@
}
.refundItem {
width: 223px;
// width: 223px;
width: 100%;
box-sizing: border-box;
margin: 12px;
padding: 8px;
font-size: small;
@ -77,7 +79,7 @@
margin-right: 4px;
}
}
.reason {
color: var(--oak-color-error);
}

View File

@ -72,7 +72,12 @@ const attrUpdateMatrix: AttrUpdateMatrix<EntityDict> = {
}
},
externalId: {
actions: ['startPaying', 'succeedPaying'],
actions: ['startPaying', 'succeedPaying', 'update'],
filter: {
iState: {
$in: ['unpaid', 'paying'],
},
},
},
},
refund: {

View File

@ -54,7 +54,15 @@ const i18ns: I18n[] = [
language: "zh-CN",
module: "oak-pay-business",
position: "src/components/accountOper/list",
data: {}
data: {
"history": "账户历史",
"chooseMonth": "选择月份",
"type": {
"both": "全部",
"in": "仅收入",
"out": "仅支出"
}
}
},
{
id: "65d19e4723a89c25e3c10a87013af430",
@ -200,11 +208,15 @@ const i18ns: I18n[] = [
},
"code": {
"label": "转帐凭证号",
"help": "请在转帐留言中附上转帐凭证号,便于人工核对"
"help": "请在转帐时于留言中附上转帐凭证号,便于人工核对。若转账时忘记附上,则请准确填写转账流水号。"
},
"channel": {
"change": "更换",
"prefix": "渠道"
},
"externalId": {
"label": "转账流水号",
"help": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -451,7 +463,8 @@ const i18ns: I18n[] = [
"overflowManualAmount": "提现额度不能超过人工划款的额度"
},
"innerLogic": "自动运算",
"gotoHistory": "提现历史"
"gotoHistory": "提现历史",
"gotoWaManage": "提现账户管理"
}
},
{
@ -698,6 +711,7 @@ const i18ns: I18n[] = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

@ -16,7 +16,7 @@ export interface Schema extends EntityShape {
paid: Price;
refunded: Price;
title: String<32>;
desc: String<64>;
desc: Text;
timeoutAt: Datetime;
creator: User;
entity: String<32>;

View File

@ -46,6 +46,7 @@
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"