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

feat:销售订单-提交审核

parent bed1cdf6
......@@ -8,4 +8,21 @@ export const orderTypeLabel = ['',
'渠道直采',
'渠道现货',
'渠道现货',
]
\ No newline at end of file
]
/**
* 销售订单状态枚举
*/
export enum SALE_ORDER_STATUS {
PREVIEW_ORDER = -1,
READY_APPROVED_ORDER,
ONE_LEVEL_APPROVED_ORDER,
TWO_LEVEL_APPROVED_ORDER,
CONFIRM_ORDER,
CONFIRM_PAY_RESULT_ORDER,
ADD_SALE_ORDER,
ADD_LOG_ORDER,
CONFIRM_DELIVE_GOODS_ORDER,
CONFIRM_RETURN_ORDER,
RETURN_DOCUMENT_ORDER
}
\ No newline at end of file
import React from 'react'
import React, { useRef } from 'react'
import { useCallback, useState, useEffect } from 'react'
import { usePageStatus } from '@/hooks/usePageStatus'
import { PublicApi } from '@/services/api'
......@@ -17,22 +17,22 @@ interface OrderDetailHookProps {
export const useOrderDetail = (options: OrderDetailHookProps) => {
// 订单详情内容
const [formData, setFormData] = useState<any>(null)
// 支付信息列表
const [payList, setPaylist] = useState<any[]>([])
const { id } = usePageStatus()
const { type } = options
const detailList = [
const dataRef = useRef<any>([
{ 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} /> },
]
{ label: '外部状态', name: 'externalState', span: 8, render: text => <StatusColors type='out' status={text}/> },
{ label: '内部状态', name: type === 'saleOrder' ? 'purchaseOrderInteriorState' : 'interiorState', span: 8, render: text => <StatusColors type={type === 'saleOrder' ? 'saleInside' : 'inside'} status={text}/> },
])
useEffect(() => {
reloadFormData()
......@@ -76,6 +76,6 @@ export const useOrderDetail = (options: OrderDetailHookProps) => {
return {
formContext,
id,
detailList
detailList: dataRef.current
}
}
\ No newline at end of file
......@@ -2,16 +2,18 @@ import React from 'react'
import AuditProcess from '@/components/AuditProcess'
import { findLastIndexFlowState } from '../../_public/order/utils'
import OrderProductTable from '../../components/orderProductTable'
import SaleOrderProductTable from '../../components/saleOrderProductTable'
import OrderPayTabs from '../../components/OrderPayTabs'
import OrderMergeInfo from '../../components/OrderMergeInfo'
import OrderDeleveRecord from '../../components/OrderDeleveRecord'
import OrderTransformRecord from '../../components/OrderTransformRecord'
export interface OrderDetailSectionProps {
formContext: any
formContext: any,
type?: 'purchaseOrder' | 'saleOrder'
}
const OrderDetailSection:React.FC<OrderDetailSectionProps> = ({formContext}) => {
const OrderDetailSection:React.FC<OrderDetailSectionProps> = ({formContext, type = 'purchaseOrder'}) => {
return (
formContext.data && <div className='gray-wrap'>
<AuditProcess
......@@ -23,7 +25,7 @@ const OrderDetailSection:React.FC<OrderDetailSectionProps> = ({formContext}) =>
innerVerifySteps={formContext.data.interiorWorkflowFlowRecordLogResponses || []}
></AuditProcess>
<OrderProductTable editable />
{ type === 'saleOrder' ? <SaleOrderProductTable/> : <OrderProductTable editable /> }
<OrderPayTabs />
......
......@@ -3,6 +3,7 @@ import { Modal } from 'antd'
import { OrderDetailContext } from '../../_public/order/context'
import { PublicApi } from '@/services/api'
import { history } from 'umi'
import { useHttpRequest } from '@/hooks/useHttpRequest'
export interface OrderElectronModalProps {
currentRef: any,
......@@ -12,6 +13,7 @@ export interface OrderElectronModalProps {
const OrderElectronModal:React.FC<OrderElectronModalProps> = (props) => {
const { currentRef } = props
const { data } = useContext(OrderDetailContext)
const { run, loading } = useHttpRequest(PublicApi.postOrderElectronicContractsAffirm)
const [visible, setVisible] = useState(false)
useEffect(() => {
currentRef.current = {
......@@ -24,7 +26,7 @@ const OrderElectronModal:React.FC<OrderElectronModalProps> = (props) => {
const params = {
id: parseInt(data.id),
}
const { code } = await PublicApi.postOrderElectronicContractsAffirm(params)
const { code } = await run(params)
if (code === 1000) {
setVisible(false)
history.goBack()
......@@ -39,11 +41,15 @@ const OrderElectronModal:React.FC<OrderElectronModalProps> = (props) => {
cancelText='不签署'
visible={visible}
onOk={handleSubmit}
confirmLoading={loading}
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>
{
data && <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>
)
}
......
.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, useCallback } from 'react'
import { Card, Table, Form, Input, Row, Col, Button, Modal } from 'antd'
import { OrderDetailContext } from '../../_public/order/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'
import { SALE_ORDER_STATUS } from '../../_public/order/constant'
import { usePageStatus } from '@/hooks/usePageStatus'
import ModalTable from '@/components/ModalTable'
import { formatTimeString } from '@/utils'
export interface OrderProductTableProps {}
// 订单商品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 warehouseColumns: any[] = [
{
dataIndex: 'freightSpaceId',
key: 'freightSpaceId',
title: '仓位ID',
align: 'center'
},
{
dataIndex: 'freightSpaceName',
key: 'freightSpaceName',
title: '仓位名称',
align: 'center'
},
{
dataIndex: 'warehouseName',
key: 'warehouseName',
title: '对应仓库',
align: 'center'
},
{
dataIndex: 'goodsName',
key: 'goodsName',
title: '对应货品',
align: 'center'
},
{
dataIndex: 'reductionInventory',
key: 'reductionInventory',
title: '扣减仓位库存',
align: 'center'
},
{
dataIndex: 'createTime',
key: 'createTime',
title: '扣减时间',
align: 'center',
render: text => formatTimeString(text)
}
]
const modalPriceActions = createFormActions()
// 总计金额联动框
export const MoneyTotalBox = ({ dataSource, isEditData }) => {
const { orderProductRequests = [], receiverAddressId } = 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 && receiverAddressId) {
// 筛选配送方式为物流的商品并且使用了运费模板
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
}, {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>运费 { isEditData && <SettingOutlined style={{marginLeft: 8}} onClick={handleSetting}/>}</div>
<div>{freePrice}</div>
</Col>
<Col span={2}>
<div>总计金额</div>
<div>{sum + freePrice}</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}
>
</ModalForm>
</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 SaleOrderProductTable:React.FC<OrderProductTableProps> = (props) => {
const { ctl, data } = useContext(OrderDetailContext)
const { page_type } = usePageStatus()
const { orderProductRequests = [] } = data || {}
const [warehouseVisible, setWarehouseVisible] = useState(false)
const [checkProductId, setCheckProductId] = useState(0) // 选中的商品id
const warehouseRef = useRef<any>({})
// 判断是否可操作当前表格
const isEditData = SALE_ORDER_STATUS.READY_APPROVED_ORDER === parseInt(page_type)
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) => {
if (asyncData.length > 0) {
const newData = orderProductRequests.map((v, i) => {
if (!v.memberPrice) {
v.memberPrice = asyncData.find(j => j.id === v.id)?.value || 0
}
return v
})
ctl.setData({
...data,
orderProductRequests: newData
})
}
})
}
}, [orderProductRequests])
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 handlePreviewWarehouse = (record) => {
setCheckProductId(record.productId)
setWarehouseVisible(true)
}
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',
editable: isEditData
},
{
title: '会员折扣',
dataIndex: 'memberPrice',
align: 'center',
key: 'memberPrice',
render: (text, record) => record.isMemberPrice ? (text + '%') : null
},
{
title: '采购数量',
dataIndex: 'purchaseCount',
align: 'center',
key: 'purchaseCount'
},
{
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) || ''
},
{
title: '仓位库存扣减记录',
dataIndex: 'record',
align: 'center',
key: 'record',
// @todo 尚未实现, 需UI设计
render: (_, record) => <Button type='link' onClick={() => handlePreviewWarehouse(record)}>查看</Button>
},
]
const fetchWarehouseData = useCallback(async (params) => {
const { data } = await PublicApi.getWarehousePositionDeductionRecordList({...params, productId: String(checkProductId)})
return data
}, [checkProductId])
const columns = 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={columns}
dataSource={orderProductRequests}
components={productComponents}
rowKey='id'
pagination={false}
/>
<MoneyTotalBox dataSource={data} isEditData={isEditData}/>
<ModalTable
columns={warehouseColumns}
modalTitle='仓位库存扣减记录'
visible={warehouseVisible}
cancel={() => setWarehouseVisible(false)}
currentRef={warehouseRef}
confirm={() => setWarehouseVisible(false)}
fetchTableData={params => fetchWarehouseData(params)}
/>
</MellowCard>
)
}
SaleOrderProductTable.defaultProps = {}
export default SaleOrderProductTable
\ No newline at end of file
......@@ -6,34 +6,17 @@ import OrderDetailHeader from '@/pages/transaction/components/OrderDetailHeader'
import OrderDetailWrapper from '@/pages/transaction/components/OrderDetailWrapper';
import PreLoading from '@/components/PreLoading';
import { useOrderDetail } from '../../../_public/order/effects/useOrderDetail';
import ApprovedOrderModal from '../../../components/approvedOrderModal';
import { history } from 'umi'
import { useHttpRequest } from '@/hooks/useHttpRequest';
import OrderDetailSection from '../../../components/orderDetailSection';
import OrderElectronModal from '@/pages/transaction/components/orderElectronModal';
const FirstApprovedOrderDetail: React.FC = () => {
const { formContext, id, detailList } = useOrderDetail({type: 'purchaseOrder'})
const { run, loading } = useHttpRequest(PublicApi.postOrderProcurementOrderReviewed)
const approvedRef = useRef<any>({})
const ReadyApprovedOrderDetail: React.FC = () => {
const { formContext, id, detailList } = useOrderDetail({type: 'saleOrder'})
const electronRef = 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()
}
})
electronRef.current.setVisible(true)
}, [])
return (
......@@ -47,19 +30,17 @@ const FirstApprovedOrderDetail: React.FC = () => {
<OrderDetailWrapper>
<PreLoading loading={!formContext.data} active paragraph={{rows: 6}}>
<OrderDetailSection formContext={formContext}/>
<OrderDetailSection formContext={formContext} type='saleOrder'/>
</PreLoading>
</OrderDetailWrapper>
{/* 提交时触发的弹窗集合 */}
<ApprovedOrderModal
currentRef={approvedRef}
onConfirm={handleSubmit}
loading={loading}
<OrderElectronModal
currentRef={electronRef}
/>
</OrderDetailContext.Provider>
</div>
);
};
export default FirstApprovedOrderDetail;
\ No newline at end of file
export default ReadyApprovedOrderDetail;
\ No newline at end of file
import React from 'react'
import { history } from 'umi'
import { Card, Button, Space, Dropdown, Menu, message } from 'antd'
import { Card, Button, Space, message } from 'antd'
import { StandardTable } from 'god'
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { PublicApi } from '@/services/api'
import { PlusCircleOutlined, DownOutlined, DeleteOutlined } from '@ant-design/icons'
import DropDeleteDown from '@/components/DropDeleteDown'
import { tableListSchema } from './schema'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import { FORM_FILTER_PATH } from '@/formSchema/const'
......@@ -13,7 +10,6 @@ import Submit from '@/components/NiceForm/components/Submit'
import { DatePicker } from '@formily/antd-components'
import { useSelfTable } from './model/useSelfTable'
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable'
import { PurchaseOrderInsideWorkState } from '@/constants'
// 待提交审核订单
......
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