Commit 1b4fecae authored by 前端-钟卫鹏's avatar 前端-钟卫鹏

feat: 添加请购单查询/待新增请购单/请购单详情页面

parent 7be4cc3e
......@@ -8,6 +8,7 @@ import { onlineBidRoute } from './onlineBid';
import { demandPlanRoute } from './demamdPlan';
import { purchasePlanRoute } from './purchasePlan';
import { PurchasDoorRoute } from './purchasDoor';
import { purchaseRequisitionRoute } from './purchaseRequisitionRoute';
/**
* 采购能力路由
......@@ -37,6 +38,8 @@ const ProcurementRoute = {
...purchaseBidRoute,
/** 在线竞价 */
...onlineBidRoute,
/** 采购请购单 */
...purchaseRequisitionRoute,
],
}
......
/**
* 采购能力 采购请购单
*
*/
export const purchaseRequisitionRoute = [
// 采购请购单
{
path: '/memberCenter/procurementAbility/purchaseRequisition',
name: 'purchaseRequisition',
routes: [
// 采购请购单查询
{
path: '/memberCenter/procurementAbility/purchaseRequisition/purchaseRequisitionList',
name: 'purchaseRequisitionSearch',
component: '@/pages/transaction/purchaseRequisition'
},
// 采购请购单详情
{
path: '/memberCenter/procurementAbility/purchaseRequisition/purchaseRequisitionList/preview',
name: 'purchaseRequisitionPreview',
hideInMenu: true,
noMargin: true,
component: '@/pages/transaction/purchaseRequisition/billPreview'
},
// 待新增请购单
{
path: '/memberCenter/procurementAbility/purchaseRequisition/readyAddBill',
name: 'readyAddBill',
component: '@/pages/transaction/purchaseRequisition/readyAddBill'
},
// 待新增请购单详情
{
path: '/memberCenter/procurementAbility/purchaseRequisition/readyAddBill/detail',
name: 'readyAddBillDetail',
component: '@/pages/transaction/purchaseRequisition/billPreview',
hideInMenu: true,
noMargin: true,
},
]
},
]
import { createContext } from 'react';
/**
* 采购能力 请购单context
*/
export const BillDetailContext = createContext<any>({})
import React, { useRef } from 'react'
import { useCallback, useState, useEffect } from 'react'
import { usePageStatus } from '@/hooks/usePageStatus'
import { PublicApi } from '@/services/api'
import { Link } from 'umi'
import { formatTimeString } from '@/utils'
import StatusColors from '../../../components/statusColors'
import { message } from 'antd'
import { history } from 'umi'
/**
* 采购能力 请购单context的effect
*/
interface BillDetailHookProps {
/** 类型预留字段 默认 requestBill */
type: 'requestBill'
}
// 请购单详情
export const useBillDetail = (options: BillDetailHookProps) => {
// 订单详情内容
const [formData, setFormData] = useState<any>(null)
// 当前的支付信息id 默认第一个
const [currentPayInfoId, setCurrentPayInfoId] = useState<any>(null)
// 支付信息列表
const [payList, setPaylist] = useState<any[]>([])
const { id } = usePageStatus()
const { orderNo } = history.location.query
const { type = 'requestBill' } = options
const dataRef = useRef<any>([
{ label: '对应报价单号', name: 'quoteNo', span: 8, render: (text, record) => <Link
to={type[0] === 's' ? `/memberCenter/tranactionAbility/inquiryOffer/offerSearch/offer/preview?id=${record?.quoteId}` : `/memberCenter/tranactionAbility/confirmOffer/offerSearch/offer/preview?id=${record?.quoteId}`}>
{text}
</Link>
},
{ label: '订单摘要', name: 'digest', span: 8 },
{ label: type[0] === 's' ? '采购会员' : '供应会员', name: type[0] === 's' ? 'buyerMemberName' : 'vendorMemberName', span: 8 },
{ label: '下单模式', name: 'orderModeName', span: 8 },
{ label: '订单类型', name: 'orderTypeName', span: 8 },
{ label: '下单时间', name: 'createTime', span: 8, render: text => formatTimeString(text) },
{
label: '外部状态',
name: 'outerStatusName',
span: 8,
render: (text, record) => <StatusColors type='out' status={text} text={record.outerStatusName} />
},
{
label: '内部状态',
name: 'innerStatusName',
span: 8,
// render: (text, record) => <StatusColors type={type} status={text} text={record.innerStatusName} />
},
{
label: '来源商城',
name: 'shopName',
span: 8,
},
])
useEffect(() => {
reloadFormData()
}, [])
// 根据type类型 调用不同的详情接口
const getDetailsApi = (type) => {
let api = null;
console.log(type, 'type')
switch (type) {
case 'purchaseOrder': api = PublicApi.getOrderBuyerDetail;
break;
case 'saleOrder': api = PublicApi.getOrderVendorDetail;
break;
case 'p_readyReceiveOrder': api = PublicApi.getOrderBuyerValidateReceiveDetail;
break;
case 's_readyConfirmDelevedOrder': api = PublicApi.getOrderVendorValidateDeliveryDetail;
break;
case 'p_readyPayOrder': api = PublicApi.getOrderBuyerValidatePayDetail;
break;
case 's_readyPayResult': api = PublicApi.getOrderVendorValidatePayConfirmDetail;
default:
api = PublicApi.getOrderBuyerDetail;
}
return api;
}
const reloadFormData = useCallback(() => {
if (id || orderNo) {
const fn = getDetailsApi(type)
// @ts-ignore
fn( id ? { orderId: id } : { orderNo }, { ctlType: "none" }).then(res => {
const { code, data, message: msg } = res
if (code === 1000) {
setFormData(data)
// // 待支付订单获取所有支付方式
// if(type === 'p_readyPayOrder') {
// reloadPayList(id)
// }
if(data.payments.length > 0){
// 过滤出未支付的 第一个
const payObj = data.payments.filter(item => item.showPayment)[0]
setCurrentPayInfoId(payObj?.paymentId ? payObj.paymentId : data.payments[0].paymentId)
}
} else {
message.error(msg)
}
})
}
}, [id])
const reloadPayList = (orderId) => {
PublicApi.getOrderBuyerValidatePayType({orderId}).then(res => {
const { code, data } = res
if (code === 1000) {
setPaylist(data)
}
})
}
// 需共享的状态
const formContext = {
data: formData,
currentPayInfoId,
payList,
ctl: {
setData: setFormData,
setPayId: setCurrentPayInfoId,
},
reloadFormData
}
return {
formContext,
id,
detailList: dataRef.current
}
}
import { getPurchaseOrderSelectOption, getSaleOrderSelectOption } from './../../effect/index';
import { ISchema } from '@formily/antd'
import { FORM_FILTER_PATH } from '@/formSchema/const'
......@@ -184,13 +183,6 @@ export const tableListSchema: any = () => {
grid: true,
},
properties: {
ctl: {
type: 'object',
'x-component': 'Children',
'x-component-props': {
children: '{{controllerBtns}}',
},
},
orderNo: {
type: 'string',
"x-component": 'Search',
......
import React from 'react';
import PreLoading from '@/components/PreLoading';
import { useBillDetail } from '../../_public/bill/effects/useBillDetail';
import { BillDetailContext } from '../../_public/bill/effects/context';
import BillDetailWrapper from '../components/billDetailWrapper';
import BillDetailHeader from '../components/billDetailHeader';
import BillDetailSection from '../components/billDetailSection';
const OrderPreview: React.FC = () => {
const { formContext, detailList } = useBillDetail({type: 'requestBill'})
const anchorTitleList = [
// { title: '流转进度', id: 'transferProcess', componentName: "TransferProcess" },
// { title: '基本信息', id: 'baseicInfo', type: "basicInfo" },
// { title: '招标物料', id: 'bidMaterial', componentName: "BidMaterial" },
// { title: '招标要求', id: 'bidNeed', type: "bidNeed" },
// { title: '报名要求', id: 'registerNeed', type: "registerNeed" },
// { title: '报名信息', id: 'registerInfoList', componentName: "RegisterInfoList", type: "registerList" },
// { title: '资格预审要求', id: 'checkNeed', type: "checkNeed" },
// { title: '资格预审信息', id: 'preCheckInfoList', componentName: "RegisterInfoList", type: "preCheckList" },
// { title: '评标要求', id: 'remarkNeed', type: "remarkNeed" },
// { title: '评标报告', id: 'remarkBidReport', componentName: "RemarkBidReport" },
// { title: '其他要求', id: 'otherNeed', type: "otherNeed" },
// { title: '招标方式', id: 'bidWay', componentName: "BidMethod" },
// { title: '招标结果', id: 'bidConfirm', componentName: "BidConfirm" },
// { title: '流转记录', id: 'transferRecord', componentName: "BidTransformRecord" },
]
return (
<div>
<BillDetailContext.Provider value={formContext}>
<BillDetailHeader
formContext={formContext}
anchorList={anchorTitleList}
/>
<BillDetailWrapper>
<PreLoading loading={!formContext.data} active paragraph={{ rows: 6 }}>
<BillDetailSection formContext={formContext} anchorList={anchorTitleList} type="requestBill" />
</PreLoading>
</BillDetailWrapper>
</BillDetailContext.Provider>
</div>
);
};
export default OrderPreview;
.titleAvator {
width:48px;
height:48px;
background:rgba(135,119,217,1);
border-radius:4px;
border:1px solid rgba(223,225,230,1);
line-height: 48px;
text-align: center;
color: #fff;
margin: 0 24px;
}
.titleAvatorText {
font-size: 16px;
font-weight: 500;
color: #303133;
margin-left: 8px;
}
.detailHeader {
background: #fff;
padding: 24px 24px 0;
:global {
.ant-row {
.ant-col-2 {
.ant-btn {
position: absolute;
top: 0;
right: 0;
}
}
}
.ant-anchor-wrapper {
min-height: auto;
}
.ant-anchor {
display: flex;
}
.ant-anchor-ink{
display: none;
}
.ant-anchor-link {
padding: 0;
text-align: center;
a {
display: inline-block;
font-size: 14px;
color: #909399;
margin: 0 16px;
height: 48px;
line-height: 48px;
}
}
.ant-anchor-link-active {
a{
font-weight: 500;
color: #303133;
border-bottom: 2px solid #00B37A;
}
}
}
}
.detailCol {
display: flex;
margin-top: 20px;
color: #303133;
}
.colLabel {
color: #909399;
margin-right: 16px;
}
// tabs标签锚点
.anchorTitle {
// height: 48px;
ul {
padding-left: 0;
display: flex;
li {
height: 48px;
line-height: 48px;
text-align: center;
list-style: none;
a {
display: inline-block;
height: 48px;
font-size: 14px;
color: #909399;
line-height: 48px;
margin: 0 16px;
}
.current {
font-weight: 500;
color: #303133;
border-bottom: 2px solid #00B37A;
}
}
}
}
.anchorTitleFixed {
position: fixed;
top: 0;
width: 100%;
z-index: 10;
:global {
.ant-row {
.ant-col-2 {
.ant-btn {
position: fixed;
top: 16px;
right: 16px;
}
}
}
}
}
import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'
import { Row, Col, Skeleton, Anchor } from 'antd'
import { history } from 'umi'
import { ArrowLeftOutlined, BugTwoTone } from '@ant-design/icons'
import style from './index.less'
const { Link } = Anchor;
export interface BillDetailHeaderProps {
extraRight?: ReactNode,
formContext?: any,
anchorList?: any,
backLink?: string,
contentRef?: any,
}
/**
* 招标详情头部
*/
const BillDetailHeader: React.FC<BillDetailHeaderProps> = ({
extraRight,
formContext,
anchorList = [],
backLink,
}) => {
const isLoading = !!formContext.data
const flagRef = useRef({
flag: false,
distanceTop: 0
})
const [current, setCurrent] = useState<number>(0)
const [isFixed, setIsFixed] = useState<boolean>(false)
useEffect(() => {
window.addEventListener("scroll", onScroll)
return (() => {
window.removeEventListener('scroll', onScroll)
})
}, [])
const onScroll = () => {
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
let floors = document.querySelectorAll(".anchorContent>div")
floors.forEach((floor: any, index: any) => {
if (floor.offsetTop - 100 <= scrollTop) {
setCurrent(index)
}
})
// 锚点导航距离顶端距离
let navDom: any = document.getElementById("anchorTitle")
if (navDom) {
let distance = navDom.offsetTop - document.documentElement.scrollTop
if (!flagRef.current.flag) {
flagRef.current.distanceTop = navDom.offsetTop
flagRef.current.flag = true
}
if (distance <= 0) {
setIsFixed(true)
}
if (document.documentElement.scrollTop <= flagRef.current.distanceTop) {
setIsFixed(false)
}
}
}
return (
<div className={isFixed ? [style.detailHeader, style.anchorTitleFixed].join(' ') : style.detailHeader} id="detailHeader">
{
isLoading ?
<Row>
{
<>
<Col span={22}>
<Row align='middle'>
<Col>
<ArrowLeftOutlined onClick={() => backLink ? history.push(backLink) : history.goBack()} />
</Col>
<Col>
<div className={style.titleAvatorText}>{formContext.data.projectName || formContext.data.inviteTender.projectName}&nbsp;|&nbsp;{formContext.data.code || formContext.data.inviteTender.code}</div>
</Col>
</Row>
<Row>
<Col>
<div className={style.anchorTitle} id="anchorTitle">
<Anchor onClick={(e) => e.preventDefault()} showInkInFixed={false} targetOffset={200}>
{
anchorList.map((item, index) => (
<Link key={index} href={`#${item['id']}`} title={item['title']} />
))
}
</Anchor>
</div>
</Col>
</Row>
</Col>
<Col span={2}>{extraRight}</Col>
</>
}
</Row>
: <Skeleton avatar={{ shape: 'square' }} active paragraph={{ rows: 3 }} />
}
</div>
)
}
BillDetailHeader.defaultProps = {}
export default BillDetailHeader
.anchorContentWrap {
div {
&:target {
padding-top: 200px;
margin-top: -200px;
}
}
}
import React, { useEffect } from 'react'
import { findLastIndexFlowState } from '@/utils'
import style from './index.less'
// import TransferProcess from '../transferProcess'
// import DescriptionsInfo from '../descriptionsInfo'
// import BidMaterial from '../bidMaterial'
// import BidMethod from '../bidMethod'
// import BidTransformRecord from '../transferRecord'
// import BidParticulars from '../bidParticulars'
// import RemarkBidReport from '../remarkBidReport'
// import ParticipateInfo from '../participateInfo'
// import BidConfirm from '../bidConfirm'
// import MemberWinInfo from '../memberWinInfo'
// import RegisterInfoList from '../registerInfoList'
export interface BillDetailSectionProps {
formContext: any,
type: 'requestBill'
anchorList?: any,
}
const BillDetailSection:React.FC<BillDetailSectionProps> = ({
formContext,
anchorList = [],
}) => {
useEffect(() => {
// 获取各个子div距父级的高度
let floors = document.querySelectorAll(".anchorContent>div")
let tempArr = []
floors.forEach((floor: any, index: any) => {
tempArr.push(floor.offsetTop)
})
// 各内容div存入context
formContext.ctl.setOffsetTopList(tempArr)
}, [])
// // 名称与组件映射
// const NameMapComponent = {
// // 流转进度组件
// "TransferProcess": TransferProcess,
// // 信息 基本信息组件
// "DescriptionsInfo": DescriptionsInfo,
// // 物料组件
// "BidMaterial": BidMaterial,
// // 招标方式组件
// "BidMethod": BidMethod,
// // 流转记录组件
// "BidTransformRecord": BidTransformRecord,
// // 中标明细组件
// "BidParticulars": BidParticulars,
// // 评标报告
// "RemarkBidReport": RemarkBidReport,
// // 会员参标信息
// "ParticipateInfo": ParticipateInfo,
// // 招标定标
// "BidConfirm": BidConfirm,
// // 会员中标信息
// "MemberWinInfo": MemberWinInfo,
// // 报名信息列表
// "RegisterInfoList": RegisterInfoList,
// }
const RenderDetailSection = ({ componentList }) => {
if(componentList && componentList.length > 0) {
return componentList.map((item, index) => (
<div key={index} id={item['id']}>{ RenderCertainContent(item) }</div>
))
} else {
return null
}
}
const RenderCertainContent = ({ title, type = null, componentName = null }) => {
let RcDom: any = null;
// switch(componentName) {
// //@todo 需另外调用接口获取数据
// case 'TransferProcess':
// RcDom = (<TransferProcess
// cardTitle={title}
// customTitleKey='name'
// customKey='id'
// outerVerifyCurrent={findLastIndexFlowState(formContext.externalWorkflowFlowRecordLogResponses)}
// innerVerifyCurrent={findLastIndexFlowState(formContext.interiorWorkflowFlowRecordLogResponses)}
// outerVerifySteps={
// formContext.externalWorkflowFlowRecordLogResponses ?
// formContext.externalWorkflowFlowRecordLogResponses.map(item => ({
// ...item,
// status: item.isActive ? 'finish' : 'wait',
// })) :
// []
// }
// innerVerifySteps={
// formContext.interiorWorkflowFlowRecordLogResponses ?
// formContext.interiorWorkflowFlowRecordLogResponses.map(item => ({
// ...item,
// status: item.isActive ? 'finish' : 'wait',
// })) :
// []
// }
// ></TransferProcess>)
// break;
// case "BidMaterial":
// RcDom = (<BidMaterial cardTitle={title} />)
// break;
// case "BidMethod":
// RcDom = (<BidMethod cardTitle={title} />)
// break;
// case "BidTransformRecord":
// RcDom = (<BidTransformRecord cardTitle={title} />)
// break;
// case "BidParticulars":
// RcDom = (<BidParticulars cardTitle={title} />)
// break;
// case "RemarkBidReport":
// RcDom = (<RemarkBidReport cardTitle={title} />)
// break;
// case "ParticipateInfo":
// RcDom = (<ParticipateInfo cardTitle={title} />)
// break;
// case "BidConfirm":
// RcDom = (<BidConfirm cardTitle={title} />)
// break;
// case "MemberWinInfo":
// RcDom = (<MemberWinInfo cardTitle={title} />)
// break;
// case "RegisterInfoList":
// RcDom = (<RegisterInfoList cardTitle={title} type={type} />)
// break;
// default:
// RcDom = (<DescriptionsInfo cardTitle={title} type={type} />)
// }
return RcDom;
}
return (
formContext.data &&
<div className={[style.anchorContentWrap, "anchorContent"].join(' ')}>
<RenderDetailSection componentList={anchorList} />
</div>
)
}
BillDetailSection.defaultProps = {}
export default BillDetailSection
import React from 'react'
import style from './index.less'
export interface BillDetailWrapperProps {}
const BillDetailWrapper:React.FC<BillDetailWrapperProps> = (props) => {
return (
<div className={style.wrapper}>{props.children}</div>
)
}
BillDetailWrapper.defaultProps = {}
export default BillDetailWrapper
.card-list {
font-size: 12px;
line-height: 20px;
margin-top: 24px;
}
.card-list_title {
font-size: 12px;
color: #909399;
}
import React, { useContext } from 'react'
import { Table, Button, Switch, Tooltip, Row, Col } from 'antd'
import MellowCard from '@/components/MellowCard'
import { BidDetailContext } from '@/pages/procurement/_public/bid/context';
import { QuestionCircleOutlined } from '@ant-design/icons';
import style from './index.less'
import BASE_CONFIG from '../../../../../config/base.config.json'
import { CALLFORBID_TYPE, INVITE_BID } from '@/constants/procurement';
import { ENTERPRISE_CENTER_URL } from '@/constants'
const shopInfo = BASE_CONFIG.web.shopInfo
/**
* 请购单 请购物料
*/
export interface BidMethodProps {
cardTitle?: string;
}
const BidMethod: React.FC<BidMethodProps> = ({cardTitle}) => {
const bidDetailContext = useContext(BidDetailContext)
const { data: _data, ctl, apiType } = bidDetailContext
// 处理和投标有关的数据格式
const data = apiType === 'callForBid' ? _data : _data.inviteTender
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (t, r, i) => ++i
},
{
title: '会员名称',
dataIndex: 'memberName',
key: 'memberName',
},
{
title: '会员类型',
dataIndex: 'memberTypeName',
key: 'memberTypeName',
},
{
title: '会员角色',
dataIndex: 'memberRoleName',
key: 'memberRoleName',
},
{
title: '是否归属会员',
dataIndex: 'isSubMember',
key: 'isSubMember',
render: (t, r) => t ? '是' : '否',
},
{
title: <>状态<Tooltip title="打开开关,审核通过后,将招标发至对应的会员"><span>&nbsp;<QuestionCircleOutlined /></span></Tooltip></>,
dataIndex: 'isSend',
key: 'isSend',
render: (text, record) => <Switch disabled defaultChecked={text} onChange={() => onChange(record)} />
},
{
title: '操作',
dataIndex: 'ctl',
key: 'ctl',
render: (text, record) => <Button type="link" target="blank" href={`${ENTERPRISE_CENTER_URL}/shop/${record.memberId}_${record.roleId}`}>进入店铺</Button>
}
];
const onChange = (record) => {
console.log(record)
}
return (
<MellowCard
title={cardTitle}
style={{marginTop: 24}}
bordered={false}
fullHeight
>
<div className={style['card-list']}>
<Row>
<Col span={2}><p className={style['card-list_title']}>招标方式</p></Col>
<Col><p>{CALLFORBID_TYPE[data.inviteTenderType]}</p></Col>
</Row>
</div>
{
data.inviteTenderType === INVITE_BID ?
<Table dataSource={data.memberList} columns={columns} pagination={{size: "small"}} />
:
<div className={style['card-list']}>
<Row>
<Col span={2}><p className={style['card-list_title']}>发布商城</p></Col>
<Col>
<p>
{
data.inviteTenderShopList.map(item => {
return shopInfo.find(_item => _item.type === item.type && _item.environment === item.environment)['name']
}).join(' / ')
}
</p>
</Col>
</Row>
</div>
}
</MellowCard>)
}
BidMethod.defaultProps = {}
export default BidMethod
.card-list {
font-size: 12px;
line-height: 20px;
margin-top: 24px;
}
.card-list_title {
font-size: 12px;
color: #909399;
}
.resultFail {
font-size: 16px;
font-weight: 500;
color: #303133;
}
.resultFailSubtitle {
font-size: 12px;
font-weight: 400;
color: #303133;
padding-left: 32px;
}
.thankModal{
:global{
.ant-modal-header {
display: none;
}
.ant-modal-content {
height: 440px;
background: url("/static/imgs/thankLetterBg.png") center center no-repeat;
// background: url('../../../../assets/imgs/thankLetterBg.png') center center no-repeat;
}
.ant-modal-footer {
display: none;
}
}
}
.thankLetter {
// width: 660px;
// height: 440px;
h2 {
text-align: center;
font-size: 24px;
font-weight: 500;
color: #303133;
// height: 36px;
// line-height: 36px;
padding-top: 20px;
}
h4 {
text-align: center;
font-size: 16px;
font-weight: 500;
color: #C0C4CC;
line-height: 16px;
padding: 0 24px;
}
p {
font-size: 14px;
font-weight: 400;
color: #303133;
line-height: 24px;
padding: 0 24px;
}
.name {
font-weight: 500;
text-align: left;
padding: 0 24px;
}
.company {
text-align: right;
padding: 0 24px;
margin-top: 100px;
}
.time {
text-align: right;
padding: 0 24px;
}
}
.cardWrap {
:global {
.ant-radio-button-wrapper:hover {
color: #606266;
}
.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) {
color: #fff;
background: #6B778C;
border-color: #6B778C;
}
.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before {
background-color: #6B778C;
}
}
}
/**
* 带按钮式Radio切换的流转进度
*/
import React, { useEffect, useState } from 'react';
import {
Steps,
Tabs,
Radio
} from 'antd';
import MellowCard from '@/components/MellowCard';
import style from './index.less'
interface TransferProcessProp {
outerVerifyCurrent?: number;
innerVerifyCurrent?: number;
outerVerifySteps?: {
step: number,
stepName: string,
operationRole: string,
status?: 'wait' | 'process' | 'finish' | 'error',
}[];
innerVerifySteps?: {
step: number,
stepName: string,
operationRole: string,
status?: 'wait' | 'process' | 'finish' | 'error',
}[];
customTitleKey?: string;
customKey?: string;
cardTitle?: string;
};
export enum TransferEnum {
/** 外部流转 */
Outer = 1,
/** 内部流转 */
Interior = 2,
}
const TransferProcess: React.FC<TransferProcessProp> = ({
outerVerifyCurrent = 0,
innerVerifyCurrent = 0,
outerVerifySteps = [],
innerVerifySteps = [],
customTitleKey,
customKey,
cardTitle = ''
}) => {
const [transferRadio, setTransferRadio] = useState<TransferEnum>(TransferEnum.Outer)
useEffect(() => {
let judgeDefault = [outerVerifySteps?.length, innerVerifySteps?.length].filter(Boolean)
if(judgeDefault.length === 1) {
if(outerVerifySteps?.length)
setTransferRadio(TransferEnum.Outer)
else
setTransferRadio(TransferEnum.Interior)
}
}, [])
const handleChangeType = (e) => {
setTransferRadio(e.target.value)
}
return (
<MellowCard
title={cardTitle}
bordered={false}
extra={
<Radio.Group value={transferRadio} buttonStyle="solid" size="small" onChange={handleChangeType}>
{outerVerifySteps?.length ? <Radio.Button value={TransferEnum.Outer}>外部流转</Radio.Button> : null}
{innerVerifySteps?.length ? <Radio.Button value={TransferEnum.Interior}>内部流转</Radio.Button> : null}
</Radio.Group>
}
className={style.cardWrap}
>
{
(outerVerifySteps?.length && transferRadio === TransferEnum.Outer) ?
<Steps style={{ marginTop: 30, overflow: "auto", paddingTop: 5, paddingBottom: 5, }} progressDot current={outerVerifyCurrent}>
{outerVerifySteps.map(item => (
<Steps.Step
key={customKey ? item[customKey] : item.step}
title={customTitleKey ? item[customTitleKey] : item.stepName}
description={item.operationRole}
status={item.status}
/>
))}
</Steps>
: null
}
{
(innerVerifySteps?.length && transferRadio === TransferEnum.Interior) ?
<Steps style={{ marginTop: 30, overflow: "auto", paddingTop: 5, paddingBottom: 5, }} progressDot current={innerVerifyCurrent}>
{innerVerifySteps.map(item => (
<Steps.Step
key={customKey ? item[customKey] : item.step}
title={customTitleKey ? item[customTitleKey] : item.stepName}
description={item.operationRole}
status={item.status}
/>
))}
</Steps>
: null
}
</MellowCard>
)
};
export default TransferProcess;
.cardWrap {
:global {
.ant-radio-button-wrapper:hover {
color: #606266;
}
.ant-radio-group-solid .ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled) {
color: #fff;
background: #6B778C;
border-color: #6B778C;
}
.ant-radio-button-wrapper-checked:not(.ant-radio-button-wrapper-disabled)::before {
background-color: #6B778C;
}
}
}
import React, { useContext, useEffect, useState } from 'react'
import { Table, Radio } from 'antd'
import { formatTimeString } from '@/utils'
import { BidDetailContext } from '@/pages/procurement/_public/bid/context';
import MellowCard from '@/components/MellowCard'
import CustomTag from '../customTag';
import { TransferEnum } from '../transferProcess';
import style from './index.less'
import { BidInOpeartTexts, BidOutOpeartTexts } from '@/constants/procurement';
/**
* 招标流转记录
*/
export interface BidTransformRecordProps {
cardTitle?: string;
}
const BidTransformRecord:React.FC<BidTransformRecordProps> = ({cardTitle}) => {
const { data, externalProcurementOrderLogResponses, interiorProcurementOrderLogResponses, apiType } = useContext(BidDetailContext)
// 根据模式选择对应的状态映射
const insideModel = apiType.indexOf('ender') !== -1 ? 'tenderInside' : 'inside';
const [transferRadio, setTransferRadio] = useState<TransferEnum>(TransferEnum.Outer)
const outReocrdCols: any[] = [
{
title: '流转顺序号',
dataIndex: 'no',
align: 'center',
key: 'no',
render: (_, __, index: number) => index + 1
},
{
title: '操作角色',
dataIndex: 'memberRoleName',
align: 'center',
key: 'memberRoleName',
},
{
title: '状态',
dataIndex: 'statusValue',
align: 'center',
key: 'statusValue',
render: (text, r) => <CustomTag text={text} color={r.statusColor} />
},
{
title: '操作',
dataIndex: 'operationValue',
align: 'center',
key: 'operationValue',
},
{
title: '操作时间',
dataIndex: 'createTime',
align: 'center',
key: 'createTime',
render: time => formatTimeString(time)
},
{
title: '审核意见',
dataIndex: 'checkRemark',
align: 'center',
key: 'checkRemark',
},
]
const insideRecordCols: any[] = [
{
title: '流转记录',
dataIndex: 'no',
align: 'center',
key: 'no',
render: (_, __, index: number) => index + 1
},
{
title: '操作人',
dataIndex: 'userName',
align: 'center',
key: 'userName',
},
{
title: '部门',
dataIndex: 'userOrgName',
align: 'center',
key: 'userOrgName',
},
{
title: '职位',
dataIndex: 'userJobTitle',
align: 'center',
key: 'userJobTitle',
},
{
title: '状态',
dataIndex: 'statusValue',
align: 'center',
key: 'statusValue',
render: (text, r) => <CustomTag text={text} color={r.statusColor} />
},
{
title: '操作',
dataIndex: 'operationValue',
align: 'center',
key: 'operationValue',
},
{
title: '操作时间',
dataIndex: 'createTime',
align: 'center',
key: 'createTime',
render: text => formatTimeString(text)
},
{
title: '审核意见',
dataIndex: 'checkRemark',
align: 'center',
key: 'checkRemark',
},
]
useEffect(() => {
let judgeDefault = [externalProcurementOrderLogResponses?.length, interiorProcurementOrderLogResponses?.length].filter(Boolean)
if(judgeDefault.length === 1) {
if(externalProcurementOrderLogResponses?.length)
setTransferRadio(TransferEnum.Outer)
else
setTransferRadio(TransferEnum.Interior)
}
}, [])
const handleChangeType = (e) => {
setTransferRadio(e.target.value)
}
return (
<MellowCard
title={cardTitle}
style={{marginTop: 24}}
bordered={false}
extra={
<Radio.Group value={transferRadio} buttonStyle="solid" size="small" onChange={handleChangeType}>
{externalProcurementOrderLogResponses?.length ? <Radio.Button value={TransferEnum.Outer}>外部流转</Radio.Button> : null}
{interiorProcurementOrderLogResponses?.length && apiType!=='tenderInCallForBid' && apiType!=='callForBidInTender' ? <Radio.Button value={TransferEnum.Interior}>内部流转</Radio.Button> : null}
</Radio.Group>
}
className={style.cardWrap}
>
{
(externalProcurementOrderLogResponses?.length && transferRadio === TransferEnum.Outer) ?
<Table
columns={outReocrdCols}
dataSource={externalProcurementOrderLogResponses}
pagination={{ size: "small" }}
rowKey="id"
/> : null
}
{
(interiorProcurementOrderLogResponses?.length && transferRadio === TransferEnum.Interior) ?
<Table
columns={insideRecordCols}
dataSource={interiorProcurementOrderLogResponses}
pagination={{ size: "small" }}
rowKey="id"
/> : null
}
</MellowCard>
)
}
BidTransformRecord.defaultProps = {}
export default BidTransformRecord
import { formatTimeString } from '@/utils'
import StatusColors from '@/pages/transaction/components/statusColors'
import EyePreview from '@/components/EyePreview'
import { history } from 'umi'
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { getSaleOrderAuditPageSelectOption } from '@/pages/transaction/effect';
import { ORDER_TYPE_CHANNEL_POINTS, ORDER_TYPE_POINTS } from '@/constants/order'
/** 采购请购单 公共列和查询schmea */
export const tableListSchema: any = (align?: String, colStyle?: Object) => {
const res = getSaleOrderAuditPageSelectOption()
if(res) {
const {
orderTypes: OrderType,
} = res
return {
type: 'object',
properties: {
orderNo: {
type: 'string',
"x-component": 'SearchFilter',
'x-component-props': {
placeholder: '请输入订单编号',
align: align || 'flex-start',
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
inline: true,
colStyle: colStyle || {marginRight: 20}
},
properties: {
digest: {
type: 'string',
'x-component-props': {
placeholder: '请输入订单摘要',
}
},
"memberName": {
type: 'string',
"x-component-props": {
placeholder: '请输入采购会员名称'
}
},
"orderType": {
type: 'string',
"x-component-props": {
placeholder: '请选择订单类型'
},
enum: OrderType.map(item => ({
label: item['text'],
value: item['id'],
}))
},
"[startDate,endDate]": {
type: 'daterange',
// "x-component": 'DateRangePickerUnix',
'x-component-props': {
placeholder: ['开始时间','结束时间'],
},
},
submit: {
'x-component': 'Submit',
'x-component-props': {
children: '查询',
},
},
},
},
}
}
}
}
export const baseOrderListColumns: any = () => {
return [
{
title: '订单号',
align: 'center',
dataIndex: 'orderNo',
key: 'orderNo',
render: (text, record) => {
return (
<EyePreview url={`${history.location.pathname}/preview?id=${record.orderId}`}>
{text}
</EyePreview>
)
}
},
{
title: '订单摘要',
align: 'center',
dataIndex: 'digest',
key: 'digest',
},
{
title: '采购会员',
align: 'center',
dataIndex: 'memberName',
key: 'memberName',
render: (t, r) => r.memberName ? t : r.buyerMemberName
},
{
title: '下单时间',
align: 'center',
dataIndex: 'createTime',
key: 'createTime',
render: (text) => formatTimeString(text)
},
{
title: '订单总额',
align: 'center',
dataIndex: 'amount',
key: 'amount',
render: (t, r) => (r.orderType === ORDER_TYPE_POINTS || r.orderType === ORDER_TYPE_CHANNEL_POINTS) ? t : `¥${t}`
},
{
title: '订单类型',
align: 'center',
dataIndex: 'orderTypeName',
key: 'orderTypeName',
},
{
title: '转单订单号',
align: 'center',
dataIndex: 'relationNo',
key: 'relationNo'
},
{
title: '外部状态',
align: 'center',
dataIndex: 'outerStatusName',
key: 'outerStatusName',
render: (text, record) => <StatusColors status={text} type='out' text={record['outerStatusName']} />,
},
{
title: '内部状态',
align: 'center',
dataIndex: 'innerStatusName',
key: 'innerStatusName',
render: (text, record) => <StatusColors status={text} type='saleInside' text={record['innerStatusName']} />,
},
]
}
import React, { useRef } from 'react'
import { Card } from 'antd'
import { StandardTable } from 'god'
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { PublicApi } from '@/services/api'
import { baseOrderListColumns } from './constant'
import { saleTableListSchema as tableListSchema } from '../_public/order/constant'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import DateRangePickerUnix from '@/components/NiceForm/components/DateRangePickerUnix'
import Submit from '@/components/NiceForm/components/Submit'
import NiceForm from '@/components/NiceForm'
import { createFormActions } from '@formily/antd'
import ModalForm from '@/components/ModalForm'
import { useHttpRequest } from '@/hooks/useHttpRequest'
import TableOperation from '@/components/TableOperation'
// 请购单查询
export interface RequestBillProps { }
const fetchTableData = async (params) => {
const { data } = await PublicApi.getOrderVendorPage(params)
return data
}
const formActions = createFormActions();
const destroyActions = createFormActions();
const pauseActions = createFormActions();
const RequestBill: React.FC<RequestBillProps> = () => {
const ref = useRef<any>({})
const destoryRef = useRef<any>({})
const pauseRef = useRef<any>({})
const { run, loading } = useHttpRequest(PublicApi.postOrderVendorCancel)
const { run: runPause, loading: loadingEnd } = useHttpRequest(PublicApi.postOrderVendorTerminate)
const fetchParams = useRef<any>({})
const loadingTableData = (params) => {
fetchParams.current = {...params}
return fetchTableData(params)
}
// 提交取消
const handleSubmit = () => {
destroyActions.submit().then(async ({values}: any) => {
const result = await run({ orderId: values.id, reason: values.cancelReason })
if (result.code === 1000) {
destroyActions.reset()
destoryRef.current.setVisible(false)
setTimeout(() => {
ref.current.reload()
}, 800)
}
})
}
// 提交中止
const handleSubmitPause = () => {
pauseActions.submit().then(async ({values}: any) => {
const result = await runPause({ orderId: values.id, reason: values.auditOpinion })
if (result.code === 1000) {
pauseRef.current.setVisible(false)
setTimeout(() => {
ref.current.reload()
}, 800)
}
})
}
const handleCancel = (r) => {
destoryRef.current.setVisible(true)
destroyActions.setFieldValue('id', r.orderId)
}
const handleSuspend = (r) => {
pauseRef.current.setVisible(true)
pauseActions.setFieldValue('id', r.orderId)
pauseActions.setFieldValue('state', 1)
}
/** 参照后台数据生成 */
const renderOptionButton = (record) => {
const buttonGroup = { '取消订单': record.showCancel, '中止': record.showTerminate }
const operationHandler = {
'取消订单': () => handleCancel(record),
'中止': () => handleSuspend(record),
}
return (
<TableOperation
buttonTextFieldMap={buttonGroup}
operationHandler={operationHandler}
/>
)
}
const secondColumns = () => {
const alreadyColumns = baseOrderListColumns()
if(alreadyColumns) {
return alreadyColumns.concat([
{
title: '操作',
align: 'center',
dataIndex: 'ctl',
key: 'ctl',
render: (text, record) => renderOptionButton(record)
}
])
}
}
return <PageHeaderWrapper>
<Card>
<StandardTable
fetchTableData={params => loadingTableData(params)}
columns={secondColumns()}
currentRef={ref}
rowKey="orderId"
controlRender={
<NiceForm
actions={formActions}
onSubmit={values => ref.current.reload(values)}
effects={($, actions) => {
useStateFilterSearchLinkageEffect(
$,
actions,
'orderNo',
FORM_FILTER_PATH,
)
}}
schema={tableListSchema()}
components={{
DateRangePickerUnix,
Submit
}}
/>
}
/>
</Card>
{/* 取消原因 */}
<ModalForm
modalTitle='取消原因'
currentRef={destoryRef}
confirm={handleSubmit}
actions={destroyActions}
schema={{
type: 'object',
properties: {
NO_SUBMIT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top',
},
properties: {
id: {
type: 'number',
title: '当前id',
visible: false,
},
cancelReason: {
type: 'textarea',
"x-component-props": {
rows: 4,
placeholder: '在此输入你的原因, 最多50个汉字'
},
title: '取消原因',
"x-rules": [
{
required: true,
message: '请输入取消原因'
},
{
limitByte: true,
maxByte: 100
}
]
}
}
}
}
}}
modalProps={{confirmLoading: loading}}
/>
{/* 中止原因 */}
<ModalForm
modalTitle='中止原因'
currentRef={pauseRef}
confirm={handleSubmitPause}
actions={pauseActions}
schema={{
type: 'object',
properties: {
NO_SUBMIT: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: 'top',
},
properties: {
id: {
type: 'number',
title: '当前id',
visible: false,
},
state: {
type: 'number',
title: '同意',
visible: false,
},
auditOpinion: {
type: 'textarea',
"x-component-props": {
rows: 4,
placeholder: '在此输入你的原因, 最多50个汉字'
},
title: '中止原因',
"x-rules": [
{
required: true,
message: '请输入取消原因'
},
{
limitByte: true,
maxByte: 100
}
]
}
}
}
}
}}
modalProps={{confirmLoading: loadingEnd}}
/>
</PageHeaderWrapper>
}
RequestBill.defaultProps = {}
export default RequestBill
import React, { useRef } from 'react'
import { history } from 'umi'
import { Card, Button, Space } from 'antd'
import { StandardTable } from 'god'
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import { PublicApi } from '@/services/api'
import { PlusCircleOutlined } from '@ant-design/icons'
import { tableListSchema } from './schema'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import Submit from '@/components/NiceForm/components/Submit'
import { useSelfTable } from './model/useReadyAddBill'
import DateRangePickerUnix from '@/components/NiceForm/components/DateRangePickerUnix'
import '../index.less'
// 待新增请购单
export interface ReadyAddBillProps {}
const fetchTableData = async (params) => {
const { data } = await PublicApi.getOrderBuyerCreatePage(params)
return data
}
const ReadyAddBill:React.FC<ReadyAddBillProps> = () => {
const currentRef = useRef<any>({})
const { columns, ref } = useSelfTable()
const clickAdd = () => {
currentRef.current.setVisible(true)
}
return <PageHeaderWrapper>
<Card>
<StandardTable
fetchTableData={params => fetchTableData(params)}
columns={columns}
currentRef={ref}
rowKey="orderId"
formilyLayouts={{
justify: 'space-between'
}}
formilyProps={{
ctx: {
inline: false,
schema: tableListSchema(),
effects: ($, actions) => {
useStateFilterSearchLinkageEffect(
$,
actions,
'orderNo',
FORM_FILTER_PATH,
);
},
components: {
DateRangePickerUnix,
Submit
}
},
layouts: {
order: 2,
span: 16
}
}}
formilyChilds={{
children: <Space>
<Button
icon={<PlusCircleOutlined/>}
type='primary'
onClick={clickAdd}
>
新建
</Button>
</Space>,
layouts: {
span: 8
}
}}
/>
</Card>
</PageHeaderWrapper>
}
ReadyAddBill.defaultProps = {}
export default ReadyAddBill
import React, { useRef } from 'react'
import { baseOrderListColumns } from '../../constant'
import { PublicApi } from '@/services/api'
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable'
import { history } from 'umi'
import TableOperation from '@/components/TableOperation'
// 待新增请购单 Hook
export const useSelfTable = () => {
const ref = useRef<any>({})
const [rowSelection, rowSelectionCtl] = useRowSelectionTable({customKey: 'orderId'})
const handleSubmit = async (id) => {
await PublicApi.postOrderBuyerCreateSubmit({orderId: id})
ref.current.reload()
}
const handleDelete = async (id) => {
await PublicApi.postOrderBuyerCreateDelete({orderId: id})
ref.current.reload()
}
const handleEdit = (record: any) => {
history.push(`/memberCenter/tranactionAbility/purchaseOrder/readyAddOrder/edit?id=${record.orderId}`)
}
/** 参照后台数据生成 */
const renderOptionButton = (record: any) => {
const buttonGroup = { '提交': true, '修改': record.showUpdate, '删除': true }
const operationHandler = {
'提交': () => handleSubmit(record.orderId),
'修改': () => handleEdit(record),
'删除': () => handleDelete(record.orderId),
}
return (
<TableOperation
buttonTextFieldMap={buttonGroup}
operationHandler={operationHandler}
/>
)
}
const secondColumns = () => {
const alreadyColumns = baseOrderListColumns()
if(alreadyColumns) {
return alreadyColumns.concat([
{
title: '操作',
align: 'center',
dataIndex: 'ctl',
key: 'ctl',
render: (text: any, record: any) => renderOptionButton(record)
}
])
}
}
return {
columns: secondColumns(),
ref,
rowSelection,
rowSelectionCtl
}
}
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
import { getPurchaseOrderReadyAddPageSelectOption } from '@/pages/transaction/effect';
export const tableListSchema: any = () => {
const res = getPurchaseOrderReadyAddPageSelectOption()
if(res) {
const {
orderTypes: OrderType,
} = res
return {
type: 'object',
properties: {
orderNo: {
type: 'string',
"x-component": 'SearchFilter',
'x-component-props': {
placeholder: '请输入订单编号',
align: 'flex-end',
},
},
[FORM_FILTER_PATH]: {
type: 'object',
'x-component': 'flex-layout',
'x-component-props': {
inline: true,
colStyle: {
marginLeft: 20
},
},
properties: {
"digest": {
type: 'string',
'x-component-props': {
placeholder: '请输入订单摘要',
}
},
"memberName": {
type: 'string',
"x-component-props": {
placeholder: '请输入供应会员名称'
}
},
"orderType": {
type: 'string',
"x-component-props": {
placeholder: '请选择订单类型'
},
enum: OrderType.map(item => ({
label: item['text'],
value: item['id'],
}))
},
"[startDate,endDate]": {
type: 'daterange',
// "x-component": 'DateRangePickerUnix',
'x-component-props': {
placeholder: ['开始时间','结束时间'],
},
},
submit: {
'x-component': 'Submit',
'x-component-props': {
children: '查询',
},
},
},
},
}
}
}
}
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