Commit 6b8f44c8 authored by 卢均锐's avatar 卢均锐

feat:采购竞价&在线竞价,新增组件,对接接口,业务逻辑

parent 8d70d5c8
......@@ -37,8 +37,8 @@ const BtnItem: React.FC<BtnItemProps> = (props: any) => {
return (
<div className={styles.btnItem2} style={{ borderColor: active ? '#00B37A' : '#F4F5F7' }}>
<div className={styles.info}>
2
<span>当前最低价:¥1,000.00</span>
{detail?.ranking}
<span>当前最低价:¥{detail?.minPrice}</span>
</div>
<div className={styles.box}>
<div className={styles.price}>
......@@ -67,7 +67,7 @@ const BtnItem: React.FC<BtnItemProps> = (props: any) => {
<Button block style={{ backgroundColor: '#F4F5F7', border: 0 }} onClick={onCancle}>取消</Button>
</Col>
<Col span={12}>
<Button type="primary" block onClick={onOk}>提交报价</Button>
<Button type="primary" block onClick={onOk} disabled={!active}>提交报价</Button>
</Col>
</Row>
</div>
......
......@@ -22,8 +22,12 @@ const BidDetailLayout: React.FC<BidDetailLayoutProps> = (props: any) => {
const { detail } = props;
const { awardProcess = [], materiels = [] } = detail;
const [showMore, setShowMore] = useState<boolean>(false);
const [activeItem, setActiveItem] = useState<any>({});
const dataSource = showMore ? [...materiels].splice(0, 4) : materiels;
const [activeItem, setActiveItem] = useState<any>(awardProcess[0] || { detailss: [] });
const dataSource = showMore ? [...activeItem.detailss].splice(0, 4) : activeItem.detailss;
useEffect(() => {
awardProcess && setActiveItem(awardProcess[0] || { detailss: [] })
},[awardProcess])
const columns: ColumnType<any>[] = [
{
......@@ -95,8 +99,8 @@ const BidDetailLayout: React.FC<BidDetailLayoutProps> = (props: any) => {
})}
</Row>
<Table dataSource={dataSource} columns={columns} pagination={false} />;
<Button type="link" block onClick={() => { setShowMore(true) }}>显示更多</Button>
<Table dataSource={dataSource} columns={columns} pagination={false} />
{!showMore && dataSource.length >4 && <Button type="link" block onClick={() => { setShowMore(true) }}>显示更多</Button>}
</Card>
</div>
)
......
import React, { useRef, useImperativeHandle, useState } from 'react';
import { StandardTable } from 'god';
import { ColumnType } from 'antd/lib/table/interface';
import { createFormActions, FormEffectHooks } from '@formily/antd';
import { Row, Col, Space, Button, Typography, Popconfirm, Badge, Tag, Menu, Drawer } from 'antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import NiceForm from '@/components/NiceForm';
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch';
import { searchSelectGetSelectCategoryOptionEffect } from '@/pages/transaction/effect/index';
import { PublicApi } from '@/services/api';
import BtnItem from '../bidDetailBtnItem';
const { Text } = Typography;
const formActions = createFormActions();
const BidProgressDrawer = (props: any) => {
const { visible, onClose, schemaType, effects, reload, fetch, quotationDetailsId, number } = props;
const tableRef = useRef<any>({});
const [activeItem, setActiveItem] = useState<any>({});
const columns: ColumnType<any>[] = [{
title: '物料编号/摘要',
key: 'quotedPriceNo',
dataIndex: 'quotedPriceNo',
render: (text: any, record: any) => (
<Space direction='vertical'>
<Text type='secondary'>{record.quotedPriceNo}</Text>
<Text type='secondary'>{record.details}</Text>
</Space>
)
}, {
title: '规格型号',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '品类',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '品牌',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '采购数量/单位',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '含税/税率',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '单价(含税)',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}, {
title: '金额(含税)',
key: 'createTime',
dataIndex: 'createTime',
render: (text: any, record: any) => text,
}];
/** 列表数据 */
const fetchData = (params?: any) => {
return new Promise((resolve, reject) => {
fetch && fetch({ id: quotationDetailsId, number: number, ...params }).then(res => {
resolve(res.data)
})
})
}
useImperativeHandle(reload, () => ({
reload: () => {
tableRef.current.reload();
}
}));
const chooseItem = (item: any) => {
if (item.id !== activeItem.id) {
setActiveItem(item);
}
}
// 搜索
const search = (values: any) => {
tableRef.current.reload(values)
}
return (
<Drawer
title="竞价过程"
width={1200}
onClose={onClose}
visible={visible}
bodyStyle={{ paddingBottom: 80 }}
footer={
<div style={{ textAlign: 'right', }}>
<Button onClick={onClose} style={{ marginRight: 8 }}>取消</Button>
<Button onClick={onClose} type="primary">确认</Button>
</div>
}
>
<Row gutter={[8, 8]} style={{ marginBottom: '10px' }}>
{[]?.map((item, index) => {
return (
<Col span={7} key={item.id} onClick={() => { chooseItem(item) }}>
<BtnItem detail={item} active={item.id === activeItem.id} />
</Col>
)
})}
</Row>
<StandardTable
currentRef={tableRef}
columns={columns}
tableProps={{ rowKew: 'id' }}
fetchTableData={(params: any) => fetchData(params)}
controlRender={
<NiceForm
actions={formActions}
onSubmit={values => search(values)}
effects={($, actions) => {
useStateFilterSearchLinkageEffect($, actions, effects, FORM_FILTER_PATH)
FormEffectHooks.onFieldChange$('category').subscribe(state => {
searchSelectGetSelectCategoryOptionEffect(actions, 'category')
})
}}
// schema={
// schemaType && SchemaRender()
// }
>
</NiceForm>
}
/>
</Drawer>
)
}
export default BidProgressDrawer
......@@ -8,10 +8,11 @@ export interface IPROPS {
id?: number,
number?: number,
fetch?: () => Promise<unknown>,
layoutTitle?: string
}
const MaterialLayout: React.FC<IPROPS> = (props: any) => {
const { id, number, fetch } = props;
const { id, number, fetch, layoutTitle } = props;
const [visible, setVisible] = useState<boolean>(false)
const currentRef = useRef({});
const [dataSource, setDataSource] = useState<any>([])
......@@ -120,7 +121,7 @@ const MaterialLayout: React.FC<IPROPS> = (props: any) => {
return (
<Card
id='materialLayout'
title='采购材料'
title={layoutTitle}
>
<StandardTable
currentRef={currentRef}
......@@ -137,4 +138,9 @@ const MaterialLayout: React.FC<IPROPS> = (props: any) => {
</Card>
)
}
MaterialLayout.defaultProps = {
layoutTitle : '采购材料'
}
export default MaterialLayout;
......@@ -5,7 +5,7 @@ import {
createFormActions,
FormEffectHooks,
} from '@formily/antd'
import { Input, Radio, DatePicker, Checkbox } from '@formily/antd-components'
import { Input, Radio, DatePicker, Checkbox } from '@formily/antd-components'
import moment from 'moment';
const actions = createFormActions()
const { onFieldChange$ } = FormEffectHooks;
......@@ -77,7 +77,6 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
x-component-props={{}}
/>
case 'abandon':
case 'discard':
return <Field
title="作废时间"
name="reasonTime"
......@@ -93,6 +92,20 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
},
}}
/>
case 'discard':
return <Field
title="作废时间"
name="reasonTime"
required
x-component="DatePicker"
x-component-props={{
style: {
width: '100%'
},
format: 'YYYY-MM-DD HH:mm:ss',
disabledDate
}}
/>
case 'date':
return <>
<Field
......@@ -112,7 +125,7 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
name="checkbox"
x-component="CheckboxGroup"
description="勾选后供应商不能再提交报价单"
enum={[{label: '立即截止报价', value: 1}]}
enum={[{ label: '立即截止报价', value: 1 }]}
/>
</>
case 'next':
......@@ -131,12 +144,12 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
/>
case 'key':
return <Field
title="请输入解密密钥"
x-component="Input"
name="password"
required
x-component-props={{}}
/>
title="请输入解密密钥"
x-component="Input"
name="password"
required
x-component-props={{}}
/>
}
}
......@@ -158,7 +171,7 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
} else if (modalType === 'abandon') {
params.reason = value.reason
params.reasonTime = new Date(value.reasonTime).getTime();
}else if(modalType === 'discard'){
} else if (modalType === 'discard') {
params.discardCaues = value.reason
params.discardTime = new Date(value.reasonTime).getTime();
} else if (modalType === 'date') {
......@@ -228,7 +241,7 @@ const ModalOperate: React.FC<IProps> = (props: any) => {
}
ModalOperate.defaultProps = {
maxNumber : 60
maxNumber: 60
}
export default ModalOperate;
......@@ -15,6 +15,8 @@ import MaterialLayout from '../../components/detail/components/materialLayout';
import BidCommonLayout from '../../components/detail/components/bidCommonLayout';
import TableCommonLayout from '../../components/detail/components/tableCommonLayout';
import LowestQuotationRecordLayout from '../../components/detail/components/lowestQuotationRecordLayout';
import BidProgressDrawer from '../../components/detail/components/bidProgressDrawer';
import {
BID_EXTERNALSTATE_COLOR,
......@@ -58,6 +60,8 @@ const SearchDetail = () => {
// 报价明细
const [quotationDetailsVisible, setQuotationDetailsVisible] = useState<boolean>(false);
const [quotationDetailsId, setQuotationDetailsId] = useState<number>();
// 竞价过程
const [progressVisible, setProgressVisible] = useState<boolean>(false);
const [dataSource, setDataSource] = useState<any>({});
// 流转数据数据
const [progressEffect, setProgressEffect] = useState<any>([]);
......@@ -246,6 +250,7 @@ const SearchDetail = () => {
id={id}
number={number}
fetch={PublicApi.getPurchaseOnlineBiddingMaterielPage}
layoutTitle='采购物料'
/>
)
}
......@@ -282,7 +287,7 @@ const SearchDetail = () => {
layoutId='quotationRecordLayout'
layoutTitle='最低报价记录'
fetch={PublicApi.getPurchaseOnlineBiddingMinimumBidding}
extra={<Button type='link'>查看竞价过程</Button>}
extra={<Button type='link' onClick={() => {setProgressVisible(false)}}>查看竞价过程</Button>}
/>
)
}
......@@ -296,8 +301,8 @@ const SearchDetail = () => {
<Fragment>
<ProgressLayout effect={progressEffect} />
{_returnWinBidMsgLayout()}
<BidCommonLayout layoutId="basicLayout" title="基本信息" effect={basicEffect} />
{_returnWinBidResultLayout()}
<BidCommonLayout layoutId="basicLayout" title="基本信息" effect={basicEffect} />
{_returnMaterialLayout()}
{_returnBidRulesLayout()}
{_returnSignUpLayout()}
......@@ -308,6 +313,12 @@ const SearchDetail = () => {
</Fragment>
}
/>
<BidProgressDrawer
effects='id'
title="竞价过程"
visible={progressVisible}
onClose={() => {setProgressVisible(false)}}
/>
</Context.Provider>
)
}
......
......@@ -34,7 +34,7 @@ const Detail = () => {
current: '1',
pageSize: '1'
}
await PublicApi.getPurchaseOnlineBiddingDetails({ ...params }).then(res => {
await PublicApi.getPurchaseOnlineBiddingBiddingDetails({ ...params }).then(res => {
if (res.code !== 1000) {
history.goBack();
return;
......
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { history } from 'umi';
import { Row, Col, Input, Drawer, Table, Space, Typography } from 'antd';
import { ColumnType } from 'antd/lib/table/interface';
......@@ -19,7 +19,12 @@ interface DetailBottomDrawerProps {
const DetailBottomDrawer: React.FC<DetailBottomDrawerProps> = (props: any) => {
const { visible, onClose, detail } = props;
const { awardProcess = [], materiels = [] } = detail;
const [activeItem, setActiveItem] = useState<any>({});
const [activeItem, setActiveItem] = useState<any>('');
const [dataSource, setDataSource] = useState<any>(materiels);
const [dataSource2, setDataSource2] = useState<any>(materiels);
useEffect(() => {
setDataSource(materiels)
}, [materiels])
const columns: ColumnType<any>[] = [
{
title: '物料编号/名称',
......@@ -47,12 +52,12 @@ const DetailBottomDrawer: React.FC<DetailBottomDrawerProps> = (props: any) => {
{
title: '含税/税率',
dataIndex: 'isHasTaxName',
render: (text: any, record: any) => <Input value={record.taxRate} onChange={(e) => record.taxRate = e.target.value} addonAfter="%" />
render: (text: any, record: any) => <Input value={record.taxRate} onChange={(e) => { _changeTax(record, e.target.value) }} addonAfter="%" />
},
{
title: '单价(含税)',
dataIndex: 'unitPrice',
render: (text: any, record: any) => <Input value={record.unitPrice} onChange={(e) => { record.unitPrice = e.target.value; record.price = _calcTotal(e.target.value, record.purchaseCount) }} addonBefore="¥" />
render: (text: any, record: any) => <Input value={record.unitPrice} onChange={(e) => { _changeUnitPrice(record, e.target.value) }} addonBefore="¥" />
},
{
title: '金额(含税)',
......@@ -61,23 +66,46 @@ const DetailBottomDrawer: React.FC<DetailBottomDrawerProps> = (props: any) => {
},
]
const _changeTax = (record: any, value: any) => {
let _dataSource = [...dataSource2];
const _i = _dataSource.findIndex((item) => item.id === record.id);
_dataSource[_i].taxRate = value;
setDataSource(_dataSource);
setDataSource2(_dataSource)
}
const _changeUnitPrice = (record: any, value: any) => {
let _dataSource = [...dataSource2];
const _i = _dataSource.findIndex((item) => item.id === record.id);
_dataSource[_i].unitPrice = value;
_dataSource[_i].price = _calcTotal(value, _dataSource[_i].purchaseCount);
setDataSource2(_dataSource)
setDataSource(_dataSource);
}
const _calcTotal = (price: any, purchaseCount: any) => {
return Number(price) * Number(purchaseCount);
return Number(price) * Number(purchaseCount) || 0;
}
const _calcCurrentTotal = () => {
return materiels?.reduce((total, cur) => total + cur.price, 0);
return dataSource2?.reduce((total, cur) => total + cur.price, 0) || 0;
}
const chooseItem = (item: any) => {
setActiveItem(item);
const chooseItem = (item?: any) => {
if (item) {
setActiveItem(item);
setDataSource(item.detailss);
} else {
setActiveItem('');
setDataSource(dataSource2);
}
}
const bidOk = () => {
const _params = {
biddingId: detail.id,
onlineId: detail.onlineId,
materiels: materiels,
materiels: dataSource2,
}
PublicApi.postPurchaseOnlineBiddingSubmitReportPrice(_params).then(res => {
if (res.code === 1000) {
......@@ -99,18 +127,18 @@ const DetailBottomDrawer: React.FC<DetailBottomDrawerProps> = (props: any) => {
closeIcon={<div>取消报价</div>}
>
<Row gutter={[8, 8]} style={{ marginBottom: '10px' }}>
<Col span={7}>
<BtnItem btnType={3} detail={{ sumPice: _calcCurrentTotal(), peportPriceSum: 1 }} active={true} onOk={bidOk} onCancle={onClose} />
<Col span={7} onClick={() => { chooseItem() }}>
<BtnItem btnType={3} detail={{ sumPice: _calcCurrentTotal(), peportPriceSum: 1 }} active={!activeItem} onOk={bidOk} onCancle={onClose} />
</Col>
{awardProcess?.map((item, index) => {
return (
<Col span={7} key={item.id} onClick={() => { chooseItem(item) }}>
<BtnItem btnType={item.id === activeItem.id ? 3 : 2} detail={item} active={item.id === activeItem.id} />
<BtnItem btnType={2} detail={item} active={item.id === activeItem.id} />
</Col>
)
})}
</Row>
<Table dataSource={materiels} columns={columns} pagination={false} />;
<Table dataSource={dataSource} columns={columns} pagination={false} />;
</Drawer>
)
}
......
......@@ -9,6 +9,7 @@ import { PublicApi } from '@/services/api';
import { formatTimeString } from '@/utils'
import Table from '../../components/table'
import ModalOperate from '../../components/modalOperate';
import {
......@@ -22,6 +23,8 @@ const ReadySubmit = () => {
/** 多选操作 */
const ref = useRef<any>({});
const [rowkeys, setRowKeys] = useState<Array<number>>([]);
const [id, setId] = useState<any>();
const [visible, setVisible] = useState<boolean>(false);
const columns: ColumnType<any>[] = [{
title: '竞价单号/摘要',
key: 'biddingNo',
......@@ -62,7 +65,7 @@ const ReadySubmit = () => {
key: 'operate',
dataIndex: 'operate',
align: 'center',
render: (text: any, record: any) => <Button onClick={() => { fetchSubmitBatch(record.id) }} type='link'>提交</Button>
render: (text: any, record: any) => <Button onClick={() => { handleExamine(record.id) }} type='link'>提交</Button>
}];
/** 批量审核 */
......@@ -79,30 +82,51 @@ const ReadySubmit = () => {
}
}
const handleExamine = (id: number) => {
setId(id);
setVisible(!visible);
}
const handleSubmit = () => {
setVisible(false);
ref.current.reload();
}
return (
<Table
selectedRow
reload={ref}
fetchRowkeys={(e) => setRowKeys(e)}
schemaType="PURCHASEBIDREADYADD_SCHEMA"
columns={columns}
effects="biddingNo"
fetch={PublicApi.getPurchaseBiddingStaySubmitList}
controllerBtns={
<Row>
<Col span={24}>
<Space size={16}>
<Button
onClick={() => fetchSubmitBatch()}
disabled={rowkeys.length === 0}
>
批量提交
<>
<Table
selectedRow
reload={ref}
fetchRowkeys={(e) => setRowKeys(e)}
schemaType="PURCHASEBIDREADYADD_SCHEMA"
columns={columns}
effects="biddingNo"
fetch={PublicApi.getPurchaseBiddingStaySubmitList}
controllerBtns={
<Row>
<Col span={24}>
<Space size={16}>
<Button
onClick={() => fetchSubmitBatch()}
disabled={rowkeys.length === 0}
>
批量提交
</Button>
</Space>
</Col>
</Row>
}
/>
</Space>
</Col>
</Row>
}
/>
<ModalOperate
id={id}
title="单据审核"
modalType="audit"
visible={visible}
fetch={PublicApi.postPurchaseBiddingSubmit}
onOk={() => handleSubmit()}
onCancel={() => setVisible(false)}
/>
</>
)
}
export default ReadySubmit
......@@ -15,6 +15,8 @@ import MaterialLayout from '../../components/detail/components/materialLayout';
import DemandLayout from '../../components/detail/components/purchaseBidDemandLayout';
import BidCommonLayout from '../../components/detail/components/bidCommonLayout';
import ModalOperate from '../../components/modalOperate';
import BidProgressDrawer from '../../components/detail/components/bidProgressDrawer';
import {
BID_EXTERNALSTATE_COLOR,
......@@ -38,7 +40,7 @@ const TABLINK = [
{ id: 'bidRulesLayout', title: '竞价规则', include: ['search', 'readyAdd', 'readyExamineOne', 'readyExamineTwo', 'readySubmit','readySubmitExamineResult','readyBid','readyExamineResultOne','readyExamineResultTwo','readyConfirm'] },
{ id: 'signUpLayout', title: '报名要求', include: ['search', 'readyAdd', 'readyExamineOne', 'readyExamineTwo', 'readySubmit', 'readyExamineSignUp','readySubmitExamineResult','readyBid','readyExamineResultOne','readyExamineResultTwo','readyConfirm'] },
{ id: 'signUpMsgLayout', title: '报名信息', include: ['search', 'readyAdd', 'readyExamineSignUp','readySubmitExamineResult','readyBid','readyExamineResultOne','readyExamineResultTwo','readyConfirm'] },
{ id: 'signUpFileLayout', title: '报名文件', include: ['readyBid','readyExamineSignUp'] },
{ id: 'signUpFileLayout', title: '报名文件', include: ['readyExamineSignUp'] },
{ id: 'conditionLayout', title: '交易条件', include: ['search', 'readyAdd', 'readyExamineOne', 'readyExamineTwo', 'readySubmit','readySubmitExamineResult','readyBid','readyExamineResultOne','readyExamineResultTwo','readyConfirm'] },
{ id: 'fileLayout', title: '附件', include: ['search', 'readyAdd','readySubmitExamineResult','readyBid','readyExamineResultOne','readyExamineResultTwo','readyConfirm'] },
{ id: 'demandLayout', title: '需求对接', include: ['search', 'readyAdd','readyBid'] },
......@@ -64,6 +66,8 @@ const SearchDetail = () => {
const [uploadBidResultVisible, setUploadBidResultVisible] = useState<boolean>(false);
// 报价明细
const [quotationDetailsVisible, setQuotationDetailsVisible] = useState<boolean>(false);
// 竞价过程
const [progressVisible, setProgressVisible] = useState<boolean>(false);
const [quotationDetailsId, setQuotationDetailsId] = useState<number>();
const [dataSource, setDataSource] = useState<any>({});
// 流转数据数据
......@@ -374,6 +378,7 @@ const SearchDetail = () => {
id={id}
number={number}
fetch={PublicApi.getPurchaseBiddingMaterielPage}
layoutTitle='采购物料'
/>
)
default:
......@@ -555,7 +560,7 @@ const SearchDetail = () => {
layoutType='result'
checkDetailFunc={_openQuotationDetailsDrawer}
effect={awardResult}
extra={<Button type='link'>查看竞价过程</Button>} />
extra={<Button type='link' onClick={() => {setProgressVisible(true)}}>查看竞价过程</Button>} />
)
default:
return null;
......@@ -616,6 +621,12 @@ const SearchDetail = () => {
visible={quotationDetailsVisible}
onClose={() => setQuotationDetailsVisible(false)}
/>
<BidProgressDrawer
effects='id'
title="竞价过程"
visible={progressVisible}
onClose={() => setProgressVisible(false)}
/>
</Context.Provider>
)
}
......
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