在pay/detail支持用户修改externalId

This commit is contained in:
Xu Chang 2024-06-17 20:20:28 +08:00
parent 09c3094f7c
commit 28c92f89e1
35 changed files with 524 additions and 78 deletions

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,26 @@ 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,
},
filters: [
{
filter() {
const { accountId } = this.props;
return {
accountId,
avail: {
$ne: 0,
},
};
}
}
@ -38,19 +42,103 @@ 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 });
}
}
});

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

@ -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": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -696,6 +708,7 @@ const i18ns = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

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

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

@ -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": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -698,6 +710,7 @@ const i18ns = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"

View File

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

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

@ -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": "转账成功后有唯一流水号,将之正确填入可减少财务人员核对时的错误概率,给您带来更大便利"
}
}
},
@ -698,6 +710,7 @@ const i18ns: I18n[] = [
"enter": "请输入",
"change": "修改",
"finish": "完成",
"loadMore": "加载更多",
"pay": {
"symbol": "¥",
"scale": "元"