Commit f1486aeb authored by 前端-许佳敏's avatar 前端-许佳敏

采购订单一级审核,二级审核调整

parent 704d12ae
......@@ -37,14 +37,14 @@ export default [
{
path: '/memberCenter/tranactionAbility/purchaseOrder/readyAddOrder/add',
name: 'addReadyAddOrder',
component: '@/pages/transaction/purchaseOrder/readyAddOrderForm',
component: '@/pages/transaction/purchaseOrder/orderCollect',
hideInMenu: true,
},
// 待新增订单-编辑订单
{
path: '/memberCenter/tranactionAbility/purchaseOrder/readyAddOrder/edit',
name: 'editReadyAddOrder',
component: '@/pages/transaction/purchaseOrder/readyAddOrderForm',
component: '@/pages/transaction/purchaseOrder/orderCollect',
hideInMenu: true,
},
......
.preWrapper {
padding: 24px;
background: #fff;
}
\ No newline at end of file
import React from 'react'
import styles from './index.less'
import { SkeletonProps } from 'antd/es/skeleton'
import { Skeleton } from 'antd'
export interface PreLoadingProps extends SkeletonProps {}
const PreLoading:React.FC<PreLoadingProps> = (props) => {
const { children, loading, ...restProps } = props
return loading ? (
<div className={styles.preWrapper}>
<Skeleton {...restProps}></Skeleton>
</div>
) : <>{children}</>
}
PreLoading.defaultProps = {}
export default PreLoading
\ No newline at end of file
......@@ -23,4 +23,8 @@
width: 100%;
height: 100%;
}
}
.white-wrap {
background: #fff;
}
\ No newline at end of file
import React, { ReactNode } from 'react'
import { Row, Col } from 'antd'
import { Row, Col, Skeleton } from 'antd'
import { history } from 'umi'
import { ArrowLeftOutlined } from '@ant-design/icons'
import style from './index.less'
export interface OrderDetailHeaderProps {
headerTitle: {
// 图标显示的文字
titleLabel: string,
titleValue: ReactNode,
picName?: string,
} | null,
extraRight?: ReactNode,
detailList?: { label: string, name: string, render?(text, record?), [key: string]: any }[],
detailData?: any
......@@ -19,41 +13,45 @@ export interface OrderDetailHeaderProps {
/**
* 订单详情头部
*/
const OrderDetailHeader:React.FC<OrderDetailHeaderProps> = ({headerTitle, extraRight, detailList = [], detailData = {}}) => {
const OrderDetailHeader:React.FC<OrderDetailHeaderProps> = ({ extraRight, detailList = [], detailData}) => {
const isLoading = !!detailData
return (
<div className={style.detailHeader}>
<Row>
{
headerTitle &&
<Col>
<Row align='middle'>
<Col>
<ArrowLeftOutlined onClick={() => history.goBack()}/>
{
isLoading ?
<Row>
{
<Col>
<Row align='middle'>
<Col>
<ArrowLeftOutlined onClick={() => history.goBack()}/>
</Col>
<Col className={style.titleAvator}></Col>
</Row>
</Col>
}
<Col style={{flex: 1}}>
<Row justify='space-between' align='middle' style={{paddingTop: 14}}>
<Col style={{flex: 1}}>
<div className={style.titleAvatorText}>订单号: {detailData.orderNo}</div>
<Row>
{detailList.map(v => {
const { label, render, name, ...colProps } = v
return detailData[name] ? <Col key={label} {...colProps} className={style.detailCol}>
<span className={style.colLabel}>{label}:</span>
{render ? render(detailData[name], detailData) : <span>{detailData[name]}</span>}
</Col> : null
})}
</Row>
</Col>
<Col className={style.titleAvator}>{headerTitle.picName}</Col>
<Col>{extraRight}</Col>
</Row>
</Col>
}
<Col style={{flex: 1}}>
<Row justify='space-between' align='middle' style={{paddingTop: 14}}>
<Col style={{flex: 1}}>
<div className={style.titleAvatorText}>{headerTitle.titleLabel}{headerTitle.titleValue}</div>
<Row>
{detailList.map(v => {
const { label, render, name, ...colProps } = v
return detailData[name] ? <Col key={label} {...colProps} className={style.detailCol}>
<span className={style.colLabel}>{label}:</span>
{render ? render(detailData[name], detailData) : <span>{detailData[name]}</span>}
</Col> : null
})}
</Row>
</Col>
<Col>{extraRight}</Col>
</Row>
</Col>
</Row>
</div>
</Row>
: <Skeleton avatar={{shape: 'square'}} active paragraph={{ rows: 3 }} />
}
</div>
)
}
......
import React from 'react'
import AuditProcess from '@/components/AuditProcess'
import { findLastIndexFlowState } from '../../utils'
import OrderProductTable from '../../public/components/orderProductTable'
import OrderPayTabs from '../../public/components/OrderPayTabs'
import OrderMergeInfo from '../../public/components/OrderMergeInfo'
import OrderDeleveRecord from '../../public/components/OrderDeleveRecord'
import OrderTransformRecord from '../../public/components/OrderTransformRecord'
export interface OrderDetailSectionProps {
formContext: any
}
const OrderDetailSection:React.FC<OrderDetailSectionProps> = ({formContext}) => {
return (
formContext.data && <div className='gray-wrap'>
<AuditProcess
customTitleKey='operationalProcess'
customKey='state'
outerVerifyCurrent={findLastIndexFlowState(formContext.data.externalWorkflowFlowRecordLogResponses)}
innerVerifyCurrent={findLastIndexFlowState(formContext.data.interiorWorkflowFlowRecordLogResponses)}
outerVerifySteps={formContext.data.externalWorkflowFlowRecordLogResponses || []}
innerVerifySteps={formContext.data.interiorWorkflowFlowRecordLogResponses || []}
></AuditProcess>
<OrderProductTable editable />
<OrderPayTabs />
<OrderMergeInfo />
<OrderDeleveRecord />
<OrderTransformRecord />
</div>
)
}
OrderDetailSection.defaultProps = {}
export default OrderDetailSection
\ No newline at end of file
import React from 'react';
import React, { useState, useCallback, useRef } from 'react';
import { OrderDetailContext } from '../../public/context';
import { PublicApi } from '@/services/api';
import { Button } from 'antd';
import OrderDetailHeader from '@/pages/transaction/components/OrderDetailHeader';
import OrderDetailWrapper from '@/pages/transaction/components/OrderDetailWrapper';
import PreLoading from '@/components/PreLoading';
import { useOrderDetail } from '../../public/effects/useOrderDetail';
import ApprovedOrderModal from '../../public/components/approvedOrderModal';
import { history } from 'umi'
import { useHttpRequest } from '@/hooks/useHttpRequest';
import OrderDetailSection from '../../components/orderDetailSection';
const FirstApprovedOrderDetail: React.FC = () => {
const { formContext, id, detailList } = useOrderDetail({type: 'purchaseOrder'})
const { run, loading } = useHttpRequest(PublicApi.postOrderProcurementOrderReviewed)
const approvedRef = useRef<any>({})
const handleClick = useCallback(() => {
approvedRef.current.setVisible(true)
}, [])
// 提交表单
const handleSubmit = useCallback(() => {
approvedRef.current.actions.submit().then(async ({values}) => {
const params = {
id: Number(id),
...values
}
const result = await run(params)
if (result.code === 1000) {
approvedRef.current.setVisible(false)
history.goBack()
}
})
}, [])
return (
<div>
待一级审核详情
<OrderDetailContext.Provider value={formContext}>
<OrderDetailHeader detailList={detailList} detailData={formContext.data} extraRight={
<Button type='primary' onClick={handleClick}>
提交审核
</Button>
} />
<OrderDetailWrapper>
<PreLoading loading={!formContext.data} active paragraph={{rows: 6}}>
<OrderDetailSection formContext={formContext}/>
</PreLoading>
</OrderDetailWrapper>
{/* 提交时触发的弹窗集合 */}
<ApprovedOrderModal
currentRef={approvedRef}
onConfirm={handleSubmit}
loading={loading}
/>
</OrderDetailContext.Provider>
</div>
);
};
......
import React, { useState, useEffect, useContext, useRef, useCallback } from 'react'
import OrderDetailHeader from '../../components/OrderDetailHeader'
import { Button, Row, Col, message, Modal } from 'antd'
import { Link, history } from 'umi'
import { Link, history, useParams } from 'umi'
import { orderTypeLabel, SALE_ORDER_STATUS, PURCHASE_ORDER_STATUS } from './constant'
import { formatTimeString } from '@/utils'
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus'
......@@ -28,15 +28,13 @@ export interface CommonOrderDetailProps { }
const approvedActions = createFormActions()
const btnApprovedTitles = [PURCHASE_ORDER_STATUS.ONE_LEVEL_APPROVED_ORDER, PURCHASE_ORDER_STATUS.TWO_LEVEL_APPROVED_ORDER]
const CommonOrderDetail: React.FC<CommonOrderDetailProps> = (props) => {
const [btnLoading, setBtnLoading] = useState<boolean>(false)
const [formData, setFormData] = useState<any>(null)
const [payList, setPaylist] = useState<any[]>([])
// 手工收货时出现的弹窗
const [confirmVisible, setConfirmVisible] = useState(false)
let { id, pageStatus, page_type = PURCHASE_ORDER_STATUS.PREVIEW_ORDER } = usePageStatus()
page_type = parseInt(page_type)
const approvedRef = useRef<any>({})
......@@ -263,7 +261,7 @@ const CommonOrderDetail: React.FC<CommonOrderDetailProps> = (props) => {
return formData ? (
<div>
<OrderDetailContext.Provider value={formContext}>
<OrderDetailHeader headerTitle={headerTiTle} detailList={detailList} detailData={formData} extraRight={extraRight()} />
<OrderDetailHeader detailList={detailList} detailData={formData} extraRight={extraRight()} />
<OrderDetailWrapper>
<div className='gray-wrap'>
{/* 工作流进度 */}
......
import React, { useEffect } from 'react'
import ModalForm from '@/components/ModalForm'
import { createFormActions } from '@formily/antd'
export interface ApprovedOrderModalProps {
currentRef: any,
onConfirm(),
loading?: boolean
}
const approvedActions = createFormActions()
// 需要弹窗确认的审核订单
const ApprovedOrderModal:React.FC<ApprovedOrderModalProps> = (props) => {
const { currentRef, onConfirm, children, loading } = props
useEffect(() => {
if (currentRef) {
currentRef.current = Object.assign({}, currentRef.current, {actions: approvedActions})
}
}, [currentRef])
return <ModalForm
modalTitle='提示'
currentRef={currentRef}
confirm={onConfirm}
actions={approvedActions}
schema={{
type: 'object',
properties: {
NO_SUBMIT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top',
},
properties: {
state: {
type: 'radio',
enum: [
{ label: '审核通过', value: 1 },
{ label: '审核不通过', value: 0 },
],
default: 1,
"x-linkages": [
{
type: 'value:visible',
target: 'cause',
condition: "{{$value === 0}}"
}
]
},
cause: {
type: 'textarea',
"x-component-props": {
rows: 4,
placeholder: '在此输入你的原因, 最多60个汉字'
},
title: '审核不通过原因',
"x-rules": [
{
required: true,
message: '请输入审核不通过原因'
},
{
limitByte: true,
maxByte: 30
}
]
}
}
}
}
}}
modalProps={{confirmLoading: loading}}
/>
}
ApprovedOrderModal.defaultProps = {}
export default ApprovedOrderModal
\ No newline at end of file
import React, { useContext } from 'react'
import { Tabs, Table, Button } from 'antd'
import StatusColors from '@/pages/transaction/components/StatusColors'
import { formatTimeString } from '@/utils'
import { OrderDetailContext } from '../../context'
import MellowCard from '@/components/MellowCard'
import NestTable from '@/components/NestTable'
import { SaleOrderInsideWorkState, DeliverySideState } from '@/constants'
import { PublicApi } from '@/services/api'
export interface OrderDeleveRecordProps {}
const outOrderCols: any[] = [
{
title: '商品ID',
dataIndex: 'productId',
align: 'center',
key: 'productId'
},
{
title: '商品名称',
dataIndex: 'productName',
align: 'center',
key: 'productName',
},
{
title: '品类',
dataIndex: 'category',
align: 'center',
key: 'category'
},
{
title: '品牌',
dataIndex: 'brand',
align: 'center',
key: 'brand',
},
{
title: '单位',
dataIndex: 'unit',
align: 'center',
key: 'unit'
},
{
title: '单价',
dataIndex: 'unitPrice',
align: 'center',
key: 'unitPrice',
},
{
title: '采购数量',
dataIndex: 'purchaseCount',
align: 'center',
key: 'purchaseCount',
},
{
title: '含税',
dataIndex: 'no',
align: 'center',
key: 'no',
render: () => '是'
},
{
title: '金额',
dataIndex: 'price',
align: 'center',
key: 'price',
},
{
title: '已发货',
dataIndex: 'delivered',
align: 'center',
key: 'delivered',
},
{
title: '未发货',
dataIndex: 'notDelivered',
align: 'center',
key: 'notDelivered',
},
{
title: '已收货',
dataIndex: 'goodsReceived',
align: 'center',
key: 'goodsReceived',
},
{
title: '差异数量',
dataIndex: 'differenceCount',
align: 'center',
key: 'differenceCount',
},
]
const sideChildrenCols: any[] = [
{
title: '商品ID',
dataIndex: 'productId',
align: 'center',
key: 'productId',
},
{
title: '商品名称',
dataIndex: 'productName',
align: 'center',
key: 'productName',
},
{
title: '品类',
dataIndex: 'category',
align: 'center',
key: 'category',
},
{
title: '品牌',
dataIndex: 'brand',
align: 'center',
key: 'brand',
},
{
title: '单位',
dataIndex: 'unit',
align: 'center',
key: 'unit',
},
{
title: '采购数量',
dataIndex: 'purchaseCount',
align: 'center',
key: 'purchaseCount',
},
{
title: '发货数量',
dataIndex: 'deliverGoodsCount',
align: 'center',
key: 'deliverGoodsCount',
},
{
title: '入库数量',
dataIndex: 'storageCount',
align: 'center',
key: 'storageCount',
},
{
title: '差异数量',
dataIndex: 'differenceCount',
align: 'center',
key: 'differenceCount',
},
]
// 订单发货记录
const OrderDeleveRecord:React.FC<OrderDeleveRecordProps> = (props) => {
const { data, reloadFormData } = useContext(OrderDetailContext)
const { orderReceivingStatisticsResponses, orderDeliveryDetailsResponses } = data
const sideOrderCols: any[] = [
{
title: '收货批次',
dataIndex: 'deliveryBatch',
align: 'center',
key: 'deliveryBatch'
},
{
title: '收货单号',
dataIndex: 'deliveryNo',
align: 'center',
key: 'deliveryNo',
},
{
title: '收货时间',
dataIndex: 'deliveryTime',
align: 'center',
key: 'deliveryTime',
render: text => formatTimeString(text)
},
{
title: '物流单号',
dataIndex: 'logisticsNo',
align: 'center',
key: 'logisticsNo',
},
{
title: '物流公司',
dataIndex: 'logisticsCompanyName',
align: 'center',
key: 'logisticsCompanyName'
},
{
title: '入库单号',
dataIndex: 'storageNo',
align: 'center',
key: 'storageNo',
},
{
title: '入库时间',
dataIndex: 'storageTime',
align: 'center',
key: 'storageTime',
render: text => formatTimeString(text)
},
{
title: '内部状态',
dataIndex: 'interiorState',
align: 'center',
key: 'interiorState',
render: text => <StatusColors type='deliveInside' status={text}/>
},
{
title: '操作',
dataIndex: 'ctl',
align: 'center',
key: 'ctl',
render: (text, record) => <>
{/* todo 需根据内部状态判断显示哪个按钮 */}
{ record.interiorState === DeliverySideState.CONFIRM_RECEIPT_ORDER && <Button type='link' onClick={() => handleReturn(record)}>确认回单</Button> }
{ record.interiorState === DeliverySideState.WAREHOUSE_ORDER && <Button type='link' onClick={() => handleConfirm(record)}>确认收货</Button> }
</>
},
]
// 确认收货
const handleConfirm = async (record) => {
const params = {
orderId: record.orderId,
id: record.id
}
await PublicApi.postOrderConfirmReceipt(params)
reloadFormData && reloadFormData()
}
// 确认回单
const handleReturn = async (record) => {
const params = {
orderId: record.orderId,
id: record.id
}
await PublicApi.postOrderReceiptOrderConfirmed(params)
reloadFormData && reloadFormData()
}
return (
<MellowCard style={{marginTop: 24}} bordered={false} id='deleveBox'>
<Tabs defaultActiveKey="2">
<Tabs.TabPane tab='订单收货统计' key="1">
<Table
columns={outOrderCols}
dataSource={orderReceivingStatisticsResponses}
pagination={false}
rowKey="productId"
/>
</Tabs.TabPane>
<Tabs.TabPane tab='订单收货明细' key="2">
<NestTable
NestColumns={[sideOrderCols, sideChildrenCols]}
rowKey='id'
childrenDataKey='orderDeliveryProducts'
dataSource={orderDeliveryDetailsResponses}
/>
</Tabs.TabPane>
</Tabs>
</MellowCard>
)
}
OrderDeleveRecord.defaultProps = {}
export default OrderDeleveRecord
\ No newline at end of file
import React, { useEffect, useState, useContext } from 'react'
import { Modal } from 'antd'
import { OrderDetailContext } from '../../context'
import { PublicApi } from '@/services/api'
import { history } from 'umi'
export interface OrderElectronModalProps {
currentRef: any,
}
//@todo 尚未完成
const OrderElectronModal:React.FC<OrderElectronModalProps> = (props) => {
const { currentRef } = props
const { data } = useContext(OrderDetailContext)
const [visible, setVisible] = useState(false)
useEffect(() => {
currentRef.current = {
visible,
setVisible
}
}, [])
const handleSubmit = async () => {
console.log('签署')
const params = {
id: parseInt(data.id),
}
const { code } = await PublicApi.postOrderElectronicContractsAffirm(params)
if (code === 1000) {
setVisible(false)
history.goBack()
}
}
return (
<Modal
width={1000}
style={{minHeight: 600}}
title='签署电子合同'
okText='签署合同并提交'
cancelText='不签署'
visible={visible}
onOk={handleSubmit}
onCancel={() => setVisible(false)}
>
<div style={{height: 600, position: 'relative'}}>
<iframe src={data.electronicContractUrl} style={{position: 'absolute', left: 0, top: 0, width: '100%', height: '100%', display: 'block'}}/>
</div>
</Modal>
)
}
OrderElectronModal.defaultProps = {}
export default OrderElectronModal
\ No newline at end of file
.card-list {
font-size: 12px;
line-height: 20px;
margin-top: 24px;
}
.card-list_title {
font-size: 12px;
color: #909399;
}
.list_radio {
border: 1px solid #fff;
}
.invoice_list_item {
display: flex;
align-items: center;
background: #F4F5F7;
font-size: 12px;
flex: 1;
margin-top: 12px;
padding: 6px 0;
&_content {
padding-left: 10px;
&_tag {
display: inline-block;
background-color: #909399;
padding: 0 6px;
height: 16px;
line-height: 16px;
color: #fff;
font-size: 12px;
&.special {
background-color: #5377ce;
}
}
&_name {
.default {
width: 64px;
height: 16px;
line-height: 16px;
text-align: center;
background: rgba(238, 238, 238, 1);
font-size: 12px;
color: #606266;
}
& > span {
margin-right: 20px;
}
}
}
&_btn_group {
display: flex;
margin-left: auto;
line-height: 14px;
// height: 14px;
.invoice_list_item_btn {
margin: 0 10px;
}
}
}
// :global {
// span.ant-radio + * {
// display: block;
// width: 100%;
// }
// .ant-radio-wrapper {
// display: flex;
// width: 100%;
// align-items: center;
// height: 72px;
// background-color: #fafafa;
// margin: 10px;
// padding-left: 16px;
// }
// .ant-radio-wrapper.ant-radio-wrapper-checked {
// border: 1px solid @main-color;
// &::after {
// content: '';
// position: absolute;
// width: 0;
// height: 0;
// border-bottom: 12px solid @main-color;
// border-left: 12px solid transparent;
// bottom: 0;
// right: 0;
// z-index: 5;
// }
// }
// }
import React, { useContext, useEffect, useState, useMemo } from 'react'
import { Row, Col, Tag } from 'antd'
import MellowCard from '@/components/MellowCard'
import { OrderDetailContext } from '../../context'
import { formatTimeString } from '@/utils'
import { DELIVERY_TYPE } from '@/constants'
import style from './index.less'
import { PublicApi } from '@/services/api'
import ContractList from '../../../../components/ContractList'
import cx from 'classnames'
export interface OrderMergeInfoProps { }
const payInfo = [
{ title: '交付日期', name: 'deliveryTime', render: (text) => formatTimeString(text) },
{
title: '交付地址', name: 'deliveryAddressInfo', render: (_, record) =>
<div>
<Row>
<Col>{record.receiverName}</Col>
<Col> / </Col>
<Col>{record.phone}</Col>
{record.isDefault && <Col style={{ marginLeft: 6 }}><Tag color='default'>默认</Tag></Col>}
</Row>
<div style={{ color: '#909399' }}>{record.fullAddress}</div>
</div>
},
{ title: '提货方式', name: 'deliveryType', render: (text) => DELIVERY_TYPE[text] },
{ title: '自提地址', name: 'pickUpAddress' },
{ title: '运费', name: 'freight' }
]
const otherInfo = [
{ title: '发票', name: 'theInvoiceInfo', render: item => <div className={style.invoice_list_item}>
<div className={style.invoice_list_item_content}>
<div className={cx(style.invoice_list_item_content_tag, item.kind !== 1 ? style.special : '')}>{item.kind === 1 ? '增值税普通发票' : '增值税专用发票'}</div>
<div className={style.invoice_list_item_content_name}>
<div>{item.invoiceTitle}</div>
<div>{item.taxNo}</div>
</div>
</div>
{
<div className={style.invoice_list_item_btn_group}>
{/* 查看功能todo */}
{/* <div className={style.invoice_list_item_btn}>查看</div> */}
</div>
}
</div>, resetCol: {flex: '1 1 100%'} },
{ title: '包装要求', name: 'pageRequire' },
{ title: '其他要求', name: 'restsRequire' },
]
const electronInfo = [
{ title: '电子合同', name: 'none' },
]
const RenderCard = ({ infoList, dataSource }) => infoList.map(v => dataSource[v.name] ? <Row key={v.name} className={style['card-list']}>
<Col span={6} className={style['card-list_title']}>{v.title}</Col>
<Col flex={1} {...v.resetCol}>{v.render ? v.render(dataSource[v.name], dataSource) : dataSource[v.name]}</Col>
</Row> : null)
const OrderMergeInfo: React.FC<OrderMergeInfoProps> = (props) => {
const orderDetailCtx = useContext(OrderDetailContext)
const { data, ctl } = orderDetailCtx
return (
<Row style={{ marginTop: 24 }} gutter={24}>
<Col span={12}>
<MellowCard title='交易信息' fullHeight>
<RenderCard infoList={payInfo} dataSource={data} />
</MellowCard>
</Col>
<Col span={6}>
<MellowCard title='其他信息' fullHeight>
<RenderCard infoList={otherInfo} dataSource={data} />
</MellowCard>
</Col>
<Col span={6}>
<MellowCard title='电子合同' fullHeight>
<ContractList
dataSource={
data.electronicContractUrl ?
[
{
electronicContractUrl: data.electronicContractUrl,
electronicContractName: data.electronicContractName,
},
] :
null
}
/>
</MellowCard>
</Col>
</Row>
)
}
OrderMergeInfo.defaultProps = {}
export default OrderMergeInfo
\ No newline at end of file
.payRadio {
min-width: 130px;
padding: 4px 6px;
background: #fff;
text-align: center;
font-size: 12px;
color: #606266;
border:1px solid rgba(235,236,240,1);
cursor: pointer;
margin-right: 12px;
margin-bottom: 12px;
&:last-child {
margin-right: 0;
}
&.active {
border-color: #00B382;
position: relative;
&::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 0;
height: 0;
border: 6px solid transparent;
border-right: 6px solid #00B382;
border-bottom: 6px solid #00B382;
}
}
}
.radioBox {
display: flex;
flex-wrap: wrap;
}
\ No newline at end of file
import React, { useState, useEffect, useContext, useRef } from 'react'
import { Modal, Steps, Row, Col, Radio, message, Upload, Button } from 'antd'
import style from './index.less'
import { OrderDetailContext } from '../../context'
import cx from 'classnames'
import { UploadOutlined } from '@ant-design/icons'
import { UPLOAD_TYPE } from '@/constants'
import { usePageStatus } from '@/hooks/usePageStatus'
import { PublicApi } from '@/services/api'
import { history } from 'umi'
export interface OrderPayModalProps {
currentRef: any,
confirm?()
}
const HeaderTitle = ({ current }) => {
return <div style={{ padding: '0 40px' }}>
<Steps size='small' current={current}>
<Steps.Step title='选择支付方式'></Steps.Step>
<Steps.Step title='进行支付'></Steps.Step>
</Steps>
</div>
}
const transformPayData = (originData, data): any => {
const result = originData.reduce((prev, next) => {
const findItem = data.find(v => v.id === next.channel) || null
if (!findItem) {
return prev
}
if (prev[next.payWay]) {
prev[next.payWay].push({ label: next.way, value: next.id })
} else {
prev[next.payWay] = [
{ label: findItem.way, value: next.id, channel: next.channel }
]
}
return prev
}, {})
return result
}
const payTextList = ["",
'线上支付',
'线下支付',
'授信支付',
'货到付款'
]
const OrderPayModal: React.FC<OrderPayModalProps> = (props) => {
const formRef = useRef<any>({})
const { id } = usePageStatus()
const { data, payList } = useContext(OrderDetailContext)
const transformPayList: any = transformPayData(data.paymentInformationResponses, payList)
const [visible, setVisible] = useState(false)
const [checked, setChecked] = useState<any>({})
const [current, setCurrent] = useState(0)
const { currentRef, confirm } = props
useEffect(() => {
if (currentRef) {
currentRef.current = {
visible,
setVisible
}
}
}, [])
useEffect(() => {
if (!visible) {
setCurrent(0)
setChecked(null)
}
}, [visible])
const handleConfirm = async () => {
if (current === 0) {
if (checked.id) {
if (checked.channel === 5) {
setCurrent(1)
} else {
message.error('暂只支持线下支付方式')
}
} else {
message.error('请先选择支付方式')
}
return;
}
// 开始提交数据
const payOrderUrls = formRef.current.urlList
const paymentInformationId = checked.id
if (!payOrderUrls || payOrderUrls.length === 0) {
message.error('请先上传凭证')
return;
}
if (!paymentInformationId) {
message.error('未选择支付方式')
return;
}
const params = {
id: Number(id),
payOrderUrls: payOrderUrls.join(','),
paymentInformationId
}
const { code } = await PublicApi.postOrderPendingOrderPay(params)
if (code === 1000) {
history.goBack()
}
confirm && confirm()
}
const handleCancel = () => {
if (current === 0) {
setVisible(false)
} else {
setCurrent(0)
}
}
const handleUploadChange = (e) => {
const { fileList } = e
const urlList = fileList.map(v => v.response && v.response.code === 1000 ? v.response.data : '')
formRef.current.urlList = urlList
}
return (
<Modal
width={704}
title={<HeaderTitle current={current} />}
visible={visible}
onOk={handleConfirm}
onCancel={handleCancel}
cancelText={current === 0 ? '取消' : '上一步'}
okText={current === 0 ? '下一步' : '确认'}
>
{current === 0 &&
transformPayList && Object.entries(transformPayList).map(([key, value]) => <Row key={key} style={{ marginBottom: 24 }}>
<Col span={6} style={{ color: '#909399', fontSize: 12 }}>{payTextList[key]}</Col>
<Col style={{ flex: 1 }}>
<div className={style.radioBox}>
{
(value as any[]).map(v =>
<div key={v.value} className={cx(style.payRadio, (checked && v.value === checked.id) ? style.active : '')} onClick={() => setChecked({ id: v.value, channel: v.channel })}>{v.label}</div>
)
}
</div>
</Col>
</Row>)
}
{
current === 1 &&
<div>
<p>上传支付凭证</p>
<Upload
name='file'
action='/api/file/file/upload'
headers={{
authorization: 'authorization-text'
}}
data={{
fileType: UPLOAD_TYPE
}}
onChange={handleUploadChange}
showUploadList
>
<Button icon={<UploadOutlined />} type='dashed'>上传凭证</Button>
</Upload>
</div>
}
</Modal>
)
}
OrderPayModal.defaultProps = {}
export default OrderPayModal
\ No newline at end of file
.fontGray {
font-size: 12px;
color: #909399;
margin-bottom: 10px;
}
.bignumber {
font-size: 24px;
color: #303133;
margin-bottom: 10px;
}
.smallnumber {
font-size: 14px;
color: #303133;
line-height: 24px;
}
\ No newline at end of file
import React, { useContext, useEffect, useState } from 'react'
import style from './index.less'
import { Tabs, Row, Col } from 'antd'
import { OrderDetailContext } from '../../context'
import MellowCard from '@/components/MellowCard'
import StatusColors from '../../../components/StatusColors'
import { PublicApi } from '@/services/api'
export interface OrderPayTabsProps {}
const TabPane = Tabs.TabPane
const TabHeader = ({dataSource}) => {
return <Row justify='space-between' style={{minWidth: 216}}>
<Col>
<div className={style.fontGray}>支付比例</div>
<div className={style.bignumber}>{dataSource.payRatio}%</div>
<div className={style.smallnumber}>{dataSource.payPrice || 0}</div>
</Col>
<Col>
<StatusColors status={5} type='out'/>
</Col>
</Row>
}
// 支付信息
const payTextList = ["",
'线上支付',
'线下支付',
'授信支付',
'货到付款'
]
const OrderPayTabs:React.FC<OrderPayTabsProps> = (props) => {
const { data, payList } = useContext(OrderDetailContext)
return (
<MellowCard style={{marginTop: 24}} bordered={false}>
<Tabs defaultActiveKey='1'>
{ data.paymentInformationResponses && data.paymentInformationResponses.map(v => <TabPane key={v.id} tab={<TabHeader dataSource={v}/>}>
<Row>
<Col className={style.fontGray} span={4}>支付环节: </Col>
<Col>{v.payNode}</Col>
</Row>
<Row>
<Col className={style.fontGray} span={4}>支付方式: </Col>
<Col>{payTextList[v.payWay]}</Col>
</Row>
<Row>
<Col className={style.fontGray} span={4}>支付渠道: </Col>
<Col>{payList.find(j => j.id === v.channel)?.way}</Col>
</Row>
</TabPane>) }
</Tabs>
</MellowCard>
)
}
OrderPayTabs.defaultProps = {}
export default OrderPayTabs
\ No newline at end of file
.cancel {
color: #909399;
font-size: 12px;
cursor: pointer;
margin: 0 4px;
}
.confirm {
color: #00B37A;
font-size: 12px;
cursor: pointer;
}
\ No newline at end of file
import React, { useContext, useState, useRef, useEffect } from 'react'
import { StandardTable } from 'god'
import { Card, Table, Form, Input, Row, Col, Button, Modal } from 'antd'
import { OrderDetailContext } from '../../context'
import { EditOutlined, SettingOutlined } from '@ant-design/icons'
import style from './index.less'
import { PublicApi } from '@/services/api'
import styled from 'styled-components'
import ModalForm from '@/components/ModalForm'
import { createFormActions } from '@formily/antd'
import MellowCard from '@/components/MellowCard'
export interface OrderProductTableProps {
editable: boolean
}
// 订单商品cell切换
const EditableContext = React.createContext<any>({});
interface Item {
key: string;
name: string;
age: string;
address: string;
}
interface EditableRowProps {
index: number;
}
const RowStyle = styled(props => <Row style={{marginTop: 12}} justify='end' {...props}>
{props.children}
</Row>)`
.ant-col {
text-align: center
}
.ant-col div {
margin-bottom: 12px;
}
`
const modalPriceActions = createFormActions()
// 总计金额联动框
export const MoneyTotalBox = ({ dataSource, preview }) => {
const { orderProductRequests = [], deliveryAddressInfo, freight = 0 } = dataSource || {}
const sum = orderProductRequests.reduce((prev, next) => prev + parseInt((next.price || 0)), 0)
// const modelRef = useRef<any>({})
// const [freePrice, setFreePrice] = useState<number>(0)
// const handleSetting = () => {
// modelRef.current.setVisible(true)
// }
// const handleConfirm = () => {
// setFreePrice(parseInt(modalPriceActions.getFieldValue('freePrice') || 0))
// modelRef.current.setVisible(false)
// }
// useEffect(() => {
// // 存在商品 并且有选择收货地址,则开始计算运费
// if (orderProductRequests && orderProductRequests.length > 0 && deliveryAddressInfo) {
// // 筛选配送方式为物流的商品并且使用了运费模板
// const logsiticsDataMaps = orderProductRequests.filter(v => v.logistics && v.logistics.useTemplate && v.logistics.deliveryType === 1)
// if (logsiticsDataMaps.length > 0) {
// PublicApi.postLogisticsFreightRemoteAddDetail({
// orderProductList: logsiticsDataMaps.map(v => ({
// templateId: v.templateId,
// weight: v.weight
// })),
// receiverAddressId: deliveryAddressInfo.id
// }, {ttl: 10 * 1000, useCache: true, ctlType: 'none'}).then(res => {
// if (res.code === 1000) {
// setFreePrice(res.data)
// }
// })
// }
// }
// }, [orderProductRequests])
return <RowStyle>
<Col span={2}>
<div>合计金额</div>
<div>{sum}</div>
</Col>
<Col span={2}>
{/* <div>运费 { !preview && <SettingOutlined style={{marginLeft: 8}} onClick={handleSetting}/> }</div> */}
<div>运费</div>
<div>{freight}</div>
</Col>
<Col span={2}>
<div>总计金额</div>
<div>{sum + freight}</div>
</Col>
{/* <ModalForm
modalTitle='设置运费'
currentRef={modelRef}
initialValues={freePrice}
schema={{
type: 'object',
properties: {
NO_SUBMIT_LAYOUT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top'
},
properties: {
freePrice: {
type: 'string',
title: '设置运费',
"x-props": {
addonBefore: '¥'
}
}
}
}
}
}}
actions={modalPriceActions}
confirm={handleConfirm}
/> */}
</RowStyle>
}
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: string;
record: Item;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<any>({});
const form = useContext(EditableContext);
useEffect(() => {
if (editing) {
inputRef.current.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async e => {
try {
const values = await form.validateFields();
values.price = parseInt(values.price)
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
const cancel = () => {
console.log('cancel')
setEditing(false)
}
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0, width: 140 }}
name={dataIndex}
rules={[
{
required: true,
message: `单价是必填的`,
},
]}
>
<Input type='number' ref={inputRef} onBlur={save} onPressEnter={save}/>
</Form.Item>
) : (
<div className="editable-cell-value-wrap" style={{ paddingRight: 24 }} onClick={toggleEdit}>
{children}
<EditOutlined/>
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
const OrderProductTable:React.FC<OrderProductTableProps> = ({editable}) => {
const { ctl, data, preview } = useContext(OrderDetailContext)
const { orderProductRequests = [] } = data || {}
const productComponents = {
body: {
row: EditableRow,
cell: EditableCell
}
}
const asyncGetMemberPrice = async (asyncData: any[]) => {
return await Promise.all(asyncData.filter(v => v.memberPrice === undefined).map(async v => {
const {code, data} = await PublicApi.getMemberManageUpperCreditParamGet({
parentMemberId: v.memberId,
parentMemberRoleId: v.memberRoleId
}, {ttl: 60 * 1000, useCache: true})
return code === 1000 ? { value: (data.parameter * 100) + '%', id: v.id } : { value: '', id: 0 }
}
))
}
useEffect(() => {
// 过滤 会员折扣不存在的列表
const hasPriceList = orderProductRequests.filter(v => !v.memberPrice)
if (hasPriceList.length > 0) {
asyncGetMemberPrice(hasPriceList).then((asyncData) => {
const newData = orderProductRequests.map((v, i) => {
if (!v.memberPrice) {
v.memberPrice = asyncData.find(j => j.id === v.id)?.value || ''
}
return v
})
ctl.setData({
...data,
orderProductRequests: newData
})
})
}
}, [])
const handleSave = row => {
const newData = [...orderProductRequests];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
ctl.setData({
...data,
orderProductRequests: newData
})
};
const productInfoColumns: any[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
},
{
title: '商品名称',
dataIndex: 'productName',
align: 'center',
key: 'productName',
},
{
title: '品类',
dataIndex: 'category',
align: 'center',
key: 'category',
},
{
title: '品牌',
dataIndex: 'brand',
align: 'center',
key: 'brand',
},
{
title: '单位',
dataIndex: 'unit',
align: 'center',
key: 'unit',
},
{
title: '单价(元)',
dataIndex: 'price',
align: 'left',
key: 'price'
},
{
title: '会员折扣',
dataIndex: 'memberPrice',
align: 'center',
key: 'memberPrice',
render: (text, record) => record.isMemberPrice ? (text + '%') : null
},
{
title: '采购数量',
dataIndex: 'purchaseCount',
align: 'center',
key: 'purchaseCount',
editable: true
},
{
title: '含税',
dataIndex: 'none',
align: 'center',
key: 'none',
render: () => '是'
},
{
title: '金额',
dataIndex: 'price',
align: 'center',
key: 'price',
},
// 接口调用
{
title: '配送方式',
dataIndex: 'logistics',
align: 'center',
key: 'logistics',
render: text => (text && text.render) || ''
}
]
const columns = !editable ? productInfoColumns : productInfoColumns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: handleSave,
}),
};
});
return (
<MellowCard title='订单商品' style={{marginTop: 24}} bordered={false}>
<Table
columns={productInfoColumns}
dataSource={orderProductRequests}
components={productComponents}
rowKey='id'
pagination={false}
/>
<MoneyTotalBox dataSource={data} preview={preview}/>
</MellowCard>
)
}
OrderProductTable.defaultProps = {}
export default OrderProductTable
\ No newline at end of file
import React, { useContext } from 'react'
import { Tabs, Table } from 'antd'
import StatusColors from '../../../components/StatusColors'
import { formatTimeString } from '@/utils'
import { OrderDetailContext } from '../../context'
import MellowCard from '@/components/MellowCard'
export interface OrderTransformRecordProps {}
const outOrderCols: any[] = [
{
title: '流转顺序号',
dataIndex: 'no',
align: 'center',
key: 'no',
render: (_, __, index: number) => index + 1
},
{
title: '操作角色',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '状态',
dataIndex: 'state',
align: 'center',
key: 'state',
render: text => <StatusColors status={text} type='out'/>
// @todo 需传递工作流状态重新render
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
key: 'operation',
},
{
title: '操作时间',
dataIndex: 'operationTime',
align: 'center',
key: 'operationTime',
render: time => formatTimeString(time)
},
{
title: '审核意见',
dataIndex: 'auditOpinion',
align: 'center',
key: 'auditOpinion',
},
]
const sideOrderCols: any[] = [
{
title: '流转记录',
dataIndex: 'no',
align: 'center',
key: 'no',
render: (_, __, index: number) => index + 1
},
{
title: '操作人',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '部门',
dataIndex: 'department',
align: 'center',
key: 'department',
},
{
title: '职位',
dataIndex: 'position',
align: 'center',
key: 'position',
},
{
title: '状态',
dataIndex: 'state',
align: 'center',
key: 'state',
render: text => <StatusColors status={text} type='inside'/>
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
key: 'operation',
},
{
title: '操作时间',
dataIndex: 'operationTime',
align: 'center',
key: 'operationTime',
},
{
title: '审核意见',
dataIndex: 'auditOpinion',
align: 'center',
key: 'auditOpinion',
},
]
// 订单流转记录
const OrderTransformRecord:React.FC<OrderTransformRecordProps> = (props) => {
const { data } = useContext(OrderDetailContext)
const { externalProcurementOrderLogResponses, interiorProcurementOrderLogResponses } = data
return (
<MellowCard style={{marginTop: 24}} bordered={false}>
<Tabs defaultActiveKey="1">
<Tabs.TabPane tab='外部订单流转记录' key="1">
<Table
columns={outOrderCols}
dataSource={externalProcurementOrderLogResponses}
pagination={false}
rowKey="id"
/>
</Tabs.TabPane>
<Tabs.TabPane tab='内部订单流转记录' key="2">
<Table
columns={outOrderCols}
dataSource={interiorProcurementOrderLogResponses}
pagination={false}
rowKey="id"
/>
</Tabs.TabPane>
</Tabs>
</MellowCard>
)
}
OrderTransformRecord.defaultProps = {}
export default OrderTransformRecord
\ No newline at end of file
import { createContext } from 'react';
export const OrderDetailContext = createContext<any>({})
\ No newline at end of file
import React from 'react'
import { useCallback, useState, useEffect } from 'react'
import { usePageStatus } from '@/hooks/usePageStatus'
import { PublicApi } from '@/services/api'
import { Link } from 'umi'
import { GlobalConfig } from '@/global/config'
import { orderTypeLabel } from '../../orderCollect/constant'
import { formatTimeString } from '@/utils'
import StatusColors from '../../components/StatusColors'
interface OrderDetailHookProps {
// 采购、销售
type: 'purchaseOrder' | 'saleOrder'
}
// 订单详情, 支持两种订单模式
export const useOrderDetail = (options: OrderDetailHookProps) => {
// 订单详情内容
const [formData, setFormData] = useState<any>(null)
// 支付信息列表
const [payList, setPaylist] = useState<any[]>([])
const { id } = usePageStatus()
const { type } = options
const detailList = [
{ label: '对应报价单号', name: 'quotationNo', span: 8, render: text => <Link to={'/'}>{text}</Link> },
{ label: '订单摘要', name: 'orderThe', span: 8 },
{ label: '供应会员', name: 'supplyMembersName', span: 8 },
{ label: '下单模式', name: 'orderModel', span: 8, render: text => GlobalConfig.web.orderMode.find(v => v.value === text)?.label || '' },
{ label: '订单类型', name: 'type', span: 8, render: text => orderTypeLabel[text] },
{ label: '下单时间', name: 'createTime', span: 8, render: text => formatTimeString(text) },
{ label: '外部状态', name: 'externalState', span: 8, render: text => <StatusColors type='out' status={text} /> },
{ label: '内部状态', name: 'interiorState', span: 8, render: text => <StatusColors type='inside' status={text} /> },
]
useEffect(() => {
reloadFormData()
reloadPayList()
}, [])
const reloadFormData = useCallback(() => {
if (id) {
const fn = type === 'purchaseOrder' ? PublicApi.getOrderProcurementOrderDetails : PublicApi.getOrderPurchaseOrderDetails
fn({ id }).then(({ data, code }) => {
if (code === 1000) {
setFormData(data)
}
})
}
}, [id])
const reloadPayList = useCallback(() => {
PublicApi.getPayPayWayList().then(res => {
const { code, data } = res
if (code === 1000) {
setPaylist(data)
}
})
}, [id])
// 需共享的状态
const formContext = {
data: formData,
payList,
ctl: {
setData: setFormData
},
reloadFormData
}
return {
formContext,
id,
detailList
}
}
\ No newline at end of file
import React from 'react';
const ReadyAddOrderForm: React.FC = () => {
return (
<div>
新增/编辑采购订单
</div>
);
};
export default ReadyAddOrderForm;
\ No newline at end of file
import React from 'react';
import React, { useState, useCallback, useRef } from 'react';
import { OrderDetailContext } from '../../public/context';
import { PublicApi } from '@/services/api';
import { Button } from 'antd';
import OrderDetailHeader from '@/pages/transaction/components/OrderDetailHeader';
import OrderDetailWrapper from '@/pages/transaction/components/OrderDetailWrapper';
import PreLoading from '@/components/PreLoading';
import { useOrderDetail } from '../../public/effects/useOrderDetail';
import ApprovedOrderModal from '../../public/components/approvedOrderModal';
import { history } from 'umi'
import { useHttpRequest } from '@/hooks/useHttpRequest';
import OrderDetailSection from '../../components/orderDetailSection';
const SecondApprovedOrderDetail: React.FC = () => {
const { formContext, id, detailList } = useOrderDetail({type: 'purchaseOrder'})
const { run, loading } = useHttpRequest(PublicApi.postOrderProcurementOrderReviewedTwo)
const approvedRef = useRef<any>({})
const handleClick = useCallback(() => {
approvedRef.current.setVisible(true)
}, [])
// 提交表单
const handleSubmit = useCallback(() => {
approvedRef.current.actions.submit().then(async ({values}) => {
const params = {
id: Number(id),
...values
}
const result = await run(params)
if (result.code === 1000) {
approvedRef.current.setVisible(false)
history.goBack()
}
})
}, [])
return (
<div>
待二级审核详情
<OrderDetailContext.Provider value={formContext}>
<OrderDetailHeader detailList={detailList} detailData={formContext.data} extraRight={
<Button type='primary' onClick={handleClick}>
提交审核
</Button>
} />
<OrderDetailWrapper>
<PreLoading loading={!formContext.data} active paragraph={{rows: 6}}>
<OrderDetailSection formContext={formContext}/>
</PreLoading>
</OrderDetailWrapper>
{/* 提交时触发的弹窗集合 */}
<ApprovedOrderModal
currentRef={approvedRef}
onConfirm={handleSubmit}
loading={loading}
/>
</OrderDetailContext.Provider>
</div>
);
};
......
......@@ -245,7 +245,7 @@ const CommonOrderDetail:React.FC<CommonOrderDetailProps> = (props) => {
return formData ? (
<div>
<OrderDetailContext.Provider value={formContext}>
<OrderDetailHeader headerTitle={headerTiTle} detailList={detailList} detailData={formData} extraRight={renderExtra()}/>
<OrderDetailHeader detailList={detailList} detailData={formData} extraRight={renderExtra()}/>
<OrderDetailWrapper>
<div className='gray-wrap'>
{/* 工作流进度 */}
......
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