Commit 6447cce8 authored by 洪浩林's avatar 洪浩林 💣
Browse files

Merge branch 'release' into 'master'

Release/v1.11.0

See merge request op/xinghuo-business/xinghuo-frontend/c-end-mweb/xh-contract-online-operation!68
parents 9400c75c 16276fad
{
"name": "xh-contract-online-operation",
"version": "1.10.1",
"version": "1.11.0",
"private": true,
"dependencies": {
"@army-knife/idcard-validation": "git+ssh://git@gitlab.xinghuolive.com:op/frontend-common/fancy-utils/idcard-validation.git#semver:^1.0",
......
......@@ -18,7 +18,7 @@ import { getRefundText } from '../../order-list.service';
import s from '../../order-list.module.scss';
import ContractUsecase from '@shared/domain/contract';
import { getTracker } from '@shared/utils/logTracker';
import SegmentCharge from '../SegementCharge/index'
const ContractOrderList = () => {
const history = useHistory();
const { batchId } = useParams<{batchId: string}>();
......@@ -62,13 +62,13 @@ const ContractOrderList = () => {
/** 是否应该显示“确认”按钮 */
const shouldShowBtnConfirm = useMemo(() => {
return hasUnConfirmOrder
return hasUnConfirmOrder
&& [OperationMode.All, OperationMode.OnlyConfirm].indexOf(operationMode) > -1
}, [hasUnConfirmOrder, operationMode])
/**
/**
* 是否应该显示支付(相关 UI)
*
*
* 1. 合同操作模式为“确认并支付”(OperationMode.All)或者“仅支付”(OperationMode.OnlyPay)
* 2. 总代付金额大于 0
**/
......@@ -139,7 +139,7 @@ const ContractOrderList = () => {
const courseAmount = totalAmount - otherAmount
return formatPrice(courseAmount)
}
const goToSigningPage = () => {
history.push(getInfoCheckingPath(batchId))
}
......@@ -212,7 +212,7 @@ const ContractOrderList = () => {
}
return true
}
const goToPreviewPage = (orderId: string, pdfFilePath: string) => {
if (checkContractFileExists(pdfFilePath)) {
// history.push(getOrderPreviewPath(batchId, orderId, pdfFilePath))
......@@ -383,6 +383,15 @@ const ContractOrderList = () => {
<span>¥{formatPrice(order.paidAmount)}</span>
</li>
</ul>
{/** 分段收费相关 */}
{ order.segmentChargeItems && order.segmentChargeItems.length && (
<SegmentCharge
orderAmount={getOrderCourseAmount(order)}
segmentPaidStatus={order.segmentPaidStatus}
segmentChargeItems={order.segmentChargeItems}
toBePaidAmount={order.toBePaidAmount}
/>
)}
<div className={s.orderCardFooter}>
{order.confirmStatus === ConfirmStatus.Confirmed && order.electronicContractAddress && (
<>
......@@ -463,4 +472,4 @@ const ContractOrderList = () => {
)
}
export default ContractOrderList
\ No newline at end of file
export default ContractOrderList
import React from 'react';
import classNames from 'classnames';
import s from '../../order-list.module.scss';
import { segmentChargeItemDetail } from '@shared/domain/common/typings';
import { SegmentPayStatus } from '@shared/domain/contract/typings';
import { formatDate } from '@shared/utils';
// 分段支付状态 -> 控制显示两阶段付款状态
const paidStatus = {
NONE: ['未付款', '未付款'],
SEG_FIRST_UNPAID: ['未付款', '未付款'],
SEG_FIRST_PAYING: ['付款中', '未付款'],
SEG_FIRST_PAID: ['已完款', '未付款'],
SEG_SECOND_PAYING: ['已完款', '付款中'],
SEG_SECOND_PAID: ['已完款', '已完款']
};
interface ISegmentProp {
orderAmount: string;
segmentPaidStatus: SegmentPayStatus;
segmentChargeItems: segmentChargeItemDetail[];
toBePaidAmount?: number; // 合同本次需要支付金额
}
const SegmentCharge = (props: ISegmentProp) => {
const {
segmentPaidStatus = SegmentPayStatus.NONE,
segmentChargeItems = [],
orderAmount = '0',
toBePaidAmount = 0,
} = props;
// 是否是只确认,只确认不显示阶段支付金额
const onlyConfirm = () => {
const { search } = window.location
const reg = new RegExp("(^|&)mode=([^&]*)(&|$)", "i");
var r = search.substr(1).match(reg);
return r && unescape(r[2]) === 'onlyConfirm'
}
// 分段订单状态没有则不显示
if (!segmentPaidStatus) return null;
const assignPaidAmount = () => {
let unTobePaid = toBePaidAmount
if (toBePaidAmount > 0) {
segmentChargeItems.forEach(item => {
// 到了收款日期,可开始收款
if (Date.now() > parseInt(item.segStartTime || '')) {
// 是否超过该阶段待收金额,如是则收剩下未支付金额
if (item.realAmount! > item.paidAmount!) {
// 应收 > 已收 => 有未收
item.pendingAmount = (unTobePaid + item.paidAmount! > item.realAmount!) ? (item.realAmount! - item.paidAmount!) : unTobePaid
item.pendingAmount = parseFloat((item.pendingAmount).toFixed(2))
// 剩余待分配金额给下一阶段支付
unTobePaid = item.realAmount! > unTobePaid ? 0 : (unTobePaid - (item.realAmount! - item.paidAmount!))
unTobePaid = parseFloat((unTobePaid).toFixed(2))
}
}
})
}
}
// 若阶段1应收金额大于等于整个合同的应收金额,则不展示分段收费的样式
if (segmentChargeItems.length) {
const [ segmentChargeItem = {} ] = segmentChargeItems;
const { realAmount: firstSegmentRealAmount = 0 } = segmentChargeItem;
if (firstSegmentRealAmount >= Number(orderAmount ?? '0')) {
return null;
}
// 将待收金额分配到两个阶段显示
assignPaidAmount()
}
return (
<>
{ segmentChargeItems.map((segment, index) => {
const {
segNo,
segStartTime,
totalAmount,
otherRealAmount,
promotionAmount,
realAmount,
paidAmount,
pendingAmount
} = segment;
return (
<div className={classNames({
[s.orderAmounts]: true,
[s.segment]: true,
[s.segmentBorder]: index === 0,
// @ts-ignore
[s.segmentActive]: paidStatus[segmentPaidStatus][index] === '付款中',
})} key={index}>
<ul>
<li>
<span>
<span className={classNames({
[s.segmentNo]: true,
// @ts-ignore
[s.segmentNoActive]: paidStatus[segmentPaidStatus][index] === '付款中',
})}>
阶段{segNo}{
// 先判断是否到开始付款时间,再判断付款状态
Date.now() > parseInt(segStartTime || '') ?
// @ts-ignore
paidStatus[segmentPaidStatus][index]
: '未开始'
}
</span>
{segStartTime && (
<span>{formatDate(new Date(segStartTime))} 开始</span>
)}
</span>
</li>
<li>
<span>阶段{segNo}课程金额</span>
<span>¥{totalAmount ?? 0}</span>
</li>
{ index === 0 && (
<li>
<span>资料费</span>
<span>¥{otherRealAmount ?? 0}</span>
</li>
)}
<li>
<span>阶段{segNo}优惠金额</span>
<span>¥{promotionAmount ?? 0}</span>
</li>
<li>
<span>阶段{segNo}应收金额</span>
<span>¥{realAmount ?? 0}</span>
</li>
{/** 阶段二未到付款时间,不显示 */}
{ (index == 0 || Date.now() > parseInt(segStartTime || '')) && (
<>
<li>
<span>已付金额</span>
<span>¥{paidAmount ?? 0}</span>
</li>
<li>
<span>本次支付</span>
<span>¥{onlyConfirm() || toBePaidAmount == 0 ? 0 : (pendingAmount ?? 0)}</span>
</li>
</>
)}
</ul>
</div>
)
})}
</>
)
}
export default SegmentCharge
......@@ -15,10 +15,10 @@ import classNames from 'classnames'
import { ProductStatus } from '@shared/domain/common/typings'
import s from '../../order-list.module.scss'
import { getTracker } from '@shared/utils/logTracker';
import SegmentCharge from '../SegementCharge/index'
const SharePayOrderList = () => {
const { batchId } = useParams<{batchId: string}>();
const [orderList, setOrderList] = useState<ISharePaymentContract[]>([])
const [isPaying, setIsPaying] = useState<boolean>(false)
......@@ -197,6 +197,15 @@ const SharePayOrderList = () => {
<span>¥{formatPrice(order.paidAmount)}</span>
</li>
</ul>
{/** 分段收费相关 */}
{ order.contractSegmentChargeItems && order.contractSegmentChargeItems.length && (
<SegmentCharge
orderAmount={getOrderCourseAmount(order)}
segmentPaidStatus={order.segmentPaidStatus}
segmentChargeItems={order.contractSegmentChargeItems}
toBePaidAmount={order.paymentAmount}
/>
)}
<div className={s.orderCardFooter}>
{totalToBeAmount > 0 ? (
<span className={s.unPaidAmount}>
......@@ -234,4 +243,4 @@ const SharePayOrderList = () => {
)
}
export default SharePayOrderList
\ No newline at end of file
export default SharePayOrderList
......@@ -76,7 +76,7 @@
line-height: 21px;
@include bold();
}
.orderClass {
position: relative;
padding: 14px 0;
......@@ -145,6 +145,58 @@
}
}
.segment {
border-bottom: none;
position: relative;
li {
padding-left: 14px;
&:first-of-type {
position: relative;
&::before{
content: '';
position: absolute;
left: 0;
top: 3px;
width: 10px;
height: 10px;
border-radius: 50%;
border: 1px solid #5c5f6b;
background: #fff;
}
}
}
}
.segmentBorder {
&::before {
content: '';
position: absolute;
left: 4px;
top: 18px;
width: 2px;
height: 250px;
border-left: 2px dashed #999;
}
}
.segmentActive {
li {
&:first-of-type {
position: relative;
&::before{
background-color: $theme-color;
border: none;
}
}
}
}
.segmentNo {
color: rgb(92, 95, 107);
margin-right: 10px;
}
.segmentNoActive {
color: $theme-color;
margin-right: 10px;
}
.orderCardFooter {
display: flex;
align-items: center;
......@@ -253,4 +305,4 @@
margin-left: 2.2vw;
}
}
}
\ No newline at end of file
}
......@@ -391,7 +391,7 @@ const InfoChecking= () => {
value={form.studentGradeId}
dataSource={gradeList}
placeholder="请选择"
onOk={(selected) => {
onOk={(selected: any[]) => {
dispatch({ key: 'studentGradeId', value: selected?.[0]?.value ?? '' })
}}
></Select>
......@@ -468,7 +468,7 @@ const InfoChecking= () => {
value={form.draweeIdCardType}
dataSource={idTypeList}
placeholder="请选择"
onOk={(selected) => {
onOk={(selected: any[]) => {
const { value = '' } = selected[0] || {};
dispatch({ key: 'draweeIdCardType', value })
......
......@@ -7,4 +7,15 @@ export enum ProductStatus {
Ended = 'ENDED', // 结束
CloseProduct = 'CLOSE_PRODUCT', // 结课(结课已退款)
Unvalid = 'UNVALID', // 无效
}
\ No newline at end of file
}
export type segmentChargeItemDetail = Partial<{
segNo: number;
segStartTime: string;
totalAmount: number;
otherRealAmount: number;
promotionAmount: number;
realAmount: number;
paidAmount: number;
pendingAmount: number;
}>
\ No newline at end of file
import { ProductStatus } from "../common/typings";
import { ProductStatus, segmentChargeItemDetail } from "../common/typings";
export enum IDTypeEnum {
// 身份证
......@@ -232,15 +232,15 @@ export enum BussinessType {
/** 获取分享支付信息 */
export interface IPaymentInfo {
orderPaymentNo: String
orderNo: string
tradeNo: string
prepayId: string
refTransNo: string
payAmount: number
createTime: string
subject: string
payInfo: string
orderPaymentNo: String
orderNo: string
tradeNo: string
prepayId: string
refTransNo: string
payAmount: number
createTime: string
subject: string
payInfo: string
blCampusId: string
}
......@@ -259,8 +259,27 @@ export interface IBatchSharePaymentInfo {
batchSharePayAmount: number;
/** 合同信息 ,ContractInfo */
contractInfos: ISharePaymentContract[];
}
/**
* 分段合同收款状态
* NONE:默认值,
* SEG_FIRST_UNPAID:阶段1未付款,
* SEG_FIRST_PAYING:阶段1付款中,
* SEG_FIRST_PAID:阶段1已完款,
* SEG_SECOND_PAYING:阶段2付款中,
* SEG_SECOND_PAID:阶段2已完款
*/
export enum SegmentPayStatus {
NONE = 'NONE',
SEG_FIRST_UNPAID = 'SEG_FIRST_UNPAID',
SEG_FIRST_PAYING = 'SEG_FIRST_PAYING',
SEG_FIRST_PAID = 'SEG_FIRST_PAID',
SEG_SECOND_PAYING = 'SEG_SECOND_PAYING',
SEG_SECOND_PAID = 'SEG_SECOND_PAID'
};
export interface ISharePaymentContract {
/** 合同id */
contractId: string;
......@@ -286,6 +305,10 @@ export interface ISharePaymentContract {
blCampusName: string;
/** 产品数量 */
productNum: number;
/** 合同分段收款状态 */
segmentPaidStatus: SegmentPayStatus;
/** 合同分段收款信息 */
contractSegmentChargeItems?: segmentChargeItemDetail[];
}
export interface ISharePaymentContractProduct {
/** 合同产品ID,相当于 IOrderProduct 的 id */
......@@ -316,4 +339,4 @@ export interface ISharePaymentContractProduct {
promotionAmount: number;
/** 合同产品状态,[,NORMAL:正常;,STARTED:开始上课;,FROZEN:冻结中;,REFUNDED:已经退款;,ENDED:结束;,CLOSE_PRODUCT:结课;,UNVALID:无效,] */
status: ProductStatus;
}
\ No newline at end of file
}
import { ProductStatus } from "../common/typings";
import { ProductStatus, segmentChargeItemDetail } from "../common/typings";
import { SegmentPayStatus } from '@shared/domain/contract/typings';
export interface IOrder {
/** 合同ID */
......@@ -30,6 +32,11 @@ export interface IOrder {
* 合同支付状态,[,UNPAY:未付款;,PAYING:付款中;,PAID:付款完成;,CANCELED:已取消;,]
* */
paidStatus: string;
/** 合同分段收款状态 */
segmentPaidStatus: SegmentPayStatus;
/** 合同分段收款信息 */
segmentChargeItems?: segmentChargeItemDetail[];
}
/** 订单对应的产品 */
......@@ -80,4 +87,4 @@ export enum PaidStatus {
Paid = 'PAID', // 已付款
Cancel = 'CANCEL', // 取消
Invalid = 'INVALID', // 失效
}
\ No newline at end of file
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment