Commit 555857bf authored by 前端-钟卫鹏's avatar 前端-钟卫鹏

fix: 添加待新增招标页面和自定义区域三级联动表单控件

parent 991b7a6f
......@@ -80,6 +80,20 @@ const memberCenterRoute = {
hideInMenu: true,
noMargin: true,
},
// 待新增招标
{
path: '/memberCenter/procurementAbility/callForBids/readyAddBid',
name: '待新增招标',
component: '@/pages/procurement/callForBids/readyAddBid',
},
// 待新增招标-新建
{
path: '/memberCenter/procurementAbility/callForBids/readyAddBid/add',
name: '新建招标',
component: '@/pages/procurement/callForBids/addNewBid',
hideInMenu: true,
noMargin: true,
}
]
},
],
......
.card-list {
font-size: 12px;
line-height: 20px;
margin-top: 12px;
}
.card-list_title {
font-size: 12px;
color: #909399;
}
// 抽屉内部内容样式
.drawerContainer {
.drawerNav {
position: fixed;
width: 160px;
height: 100%;
&:after {
content: "";
height: 100%;
width: 1px;
background-color: #F4F5F7;
position: absolute;
top: 0;
right: 0;
}
ul {
padding-left: 0;
margin: 0;
width: 160px;
list-style: none;
li {
width: 160px;
height: 48px;
line-height: 48px;
background: #FFFFFF;
a {
height: 24px;
font-size: 12px;
font-weight: 500;
color: #606266;
line-height: 24px;
padding-left: 16px;
padding-top: 6px;
padding-bottom: 6px;
}
.current {
color: #303133;
border-left: 2px solid #00B37A;
}
}
}
}
.drawerContent {
.drawerContentItem {
.drawerContentTitle {
p {
margin: 16px 0 16px 0;
font-size: 14px;
font-weight: 500;
color: #909399;
height: 14px;
line-height: 14px;
.longString {
color: #00B37A;
margin-right: 6px;
height: 14px;
line-height: 14px;
font-size: 14px;
font-weight: bolder;
}
}
}
}
}
}
import React, { ReactNode, useContext, useEffect, useRef, useState } from 'react'
import { Table, Drawer, Button, Tabs, Row, Col } from 'antd'
import style from './index.less'
/**
* 带锚点定位跳转的抽屉 Drawer
*/
export interface AnchorProps {
/** 源数据 */
data?: any,
/** 数据项和render渲染函数 */
dataList?: { title: string, name: string, render?(data?), [key: string]: any }[],
title?: string,
visible?: boolean,
/** 自定义底部布局 */
footer?: ReactNode,
/** 点击遮罩回调 */
onClose?: any,
}
const AnchorDrawer: React.FC<AnchorProps> = ({
data,
dataList,
title = "",
visible = false,
footer,
onClose,
...restProps
}) => {
const [current, setCurrent] = useState<number>(0)
const [offsetTopList, setOffsetTopList] = useState<number[]>([])
useEffect(() => {
if(visible) {
// 开启滚动事件冒泡
window.addEventListener("scroll", onScroll, true);
// 获取各个子div距父级的高度
let floors = document.querySelectorAll(".drawerContent>div")
let tempArr = []
floors.forEach((floor: any, index: any) => {
tempArr.push(floor.offsetTop)
})
setOffsetTopList(tempArr)
}
return () => window.removeEventListener('scroll', onScroll)
}, [visible])
const onScroll = (e) => {
if(e.target.className === 'ant-drawer-body') {
let scrollTop = document.querySelectorAll(".ant-drawer-body")[0].scrollTop
let floors = document.querySelectorAll(".drawerContent>div")
floors.forEach((floor: any, index: any) => {
if(floor.offsetTop - 40 <= scrollTop) {
setCurrent(index)
}
})
}
}
const handleClick = (i: number) => {
let dom = document.querySelectorAll(".ant-drawer-body")[0]
dom.scroll({
'top': offsetTopList[i] - 16,
behavior: "smooth",
})
if(offsetTopList[i] + dom.clientHeight >= dom.scrollHeight) {
setCurrent(i)
}
}
return (<>
<Drawer
title={title}
width={800}
bodyStyle={{ paddingLeft: 0, paddingTop: 0 }}
onClose={onClose}
visible={visible}
footer={footer}
{...restProps}
>
<div className={style.drawerContainer}>
<Row>
<Col span={6}>
<div className={style.drawerNav}>
<ul>
{
dataList.map((item, index) => (
<li key={index} onClick={() => handleClick(index)}><a className={current === index ? style.current : null}>{item['title']}</a></li>
))
}
</ul>
</div>
</Col>
<Col span={18}>
<div className={[style.drawerContent, 'drawerContent'].join(' ')}>
{
dataList.map((item, index) => (
<div className={style.drawerContentItem} key={index}>
<div className={style.drawerContentTitle}>
<p><span className={style.longString}>|</span>{item['title']}</p>
</div>
<div className={style.drawerContentInfo}>
{item.render ? item.render(data) : <span>{data[item.name]}</span>}
</div>
</div>
))
}
</div>
</Col>
</Row>
</div>
</Drawer>
</>)
}
AnchorDrawer.defaultProps = {}
export default AnchorDrawer
import React, { useRef, useEffect, useState } from 'react'
import { Input, Row, Col, Select, Form, Button, message } from 'antd';
import styled from 'styled-components'
import { PublicApi } from '@/services/api';
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
/**
* 自定义 省市区/县多项选择
*/
const RowStyleLayout = styled(props => <div {...props} />)`
width: 100%;
.formwrap {
position: relative;
.formbutton {
position: absolute;
right: -95px;
display: flex;
width: 90px;
:global {
.ant-form-item {
width: 32px;
margin-right: 10px;
.ant-btn {
width: 32px;
padding: 0;
margin-right: 10px;
}
}
}
}
}
`
const { Option } = Select
const MultAddress = (props) => {
const { value, mutators } = props
const {
placeholder = [],
warningText = '请完善相关信息',
...rest
} = props.props["x-component-props"] || {}
const [code, setcode] = useState<any>([]);
const [province, setprovince] = useState<any>([]); // 省列表
const [city, setcity] = useState<any>([]); // 市列表
const [area, setarea] = useState<any>([]); // 区/县列表
useEffect(() => {
getAllAreaData().then(data => {
setprovince(data)
})
}, [])
const getAllAreaData = () => {
return new Promise(resolve => {
PublicApi.getManageAreaAll().then(res => {
if (res.code === 1000) {
resolve(res.data);
}
})
})
}
const openArea = true; // 开启区县
// 触发onChange改变值
// num 1省 2市 3区/县
const changeAddress = async (val: any, idx: number, num: number) => {
const result = [...value]
const cityCode: Array<any> = []
result.forEach((item: any) => {
if (item.cityCode) {
cityCode.push(item.cityCode)
}
})
setcode([...cityCode]);
if (num === 1) {
await province.forEach(item => {
if (item.code === val) {
PublicApi.getManageAreaByPcodeAll({ pcode: val }).then((res: any) => {
if (res.code === 1000) {
result[idx].provinceCode = val;
result[idx].province = item.name;
city[idx] = { citydata: res.data }
setcity([...city])
}
})
}
})
} else if(num === 2) {
if(openArea) {
await city[idx].citydata.forEach(item => {
if (item.code === val) {
PublicApi.getManageAreaByPcodeAll({ pcode: val }).then((res: any) => {
if (res.code === 1000) {
result[idx].cityCode = val;
result[idx].city = item.name;
area[idx] = { areadata: res.data }
setarea([...area])
}
})
}
})
} else {
city.forEach(item => {
item.citydata.forEach(items => {
if (items.code === val) {
const cityCode = code.find(it => items.code);
if (cityCode !== items.code) {
items.disabled = false
}
result[idx].cityCode = val;
result[idx].city = items.name;
}
})
})
setcity([...city])
}
} else {
area.forEach(item => {
item.areadata.forEach(items => {
if (items.code === val) {
const areaCode = code.find(it => items.code);
if (areaCode !== items.code) {
items.disabled = false
}
result[idx].areaCode = val;
result[idx].area = items.name;
}
})
})
setcity([...city])
}
mutators.change(result)
}
// 触发select下拉调用
// 默认1省 2市 3区/县
const onDropdownVisibleChange = (type?: number) => {
return new Promise(reslove => reslove(code)).then((res: any) => {
if(type === 2) {
city.forEach((item: any) => {
item.citydata.filter(it => {
res.map(items => {
if (items === it.code) {
it.disabled = true
}
})
})
})
setcity([...city])
} else {
area.forEach((item: any) => {
item.areadata.filter(it => {
res.map(items => {
if (items === it.code) {
it.disabled = true
}
})
})
})
setarea([...area])
}
})
}
// 添加一条地址
const addAddress = (idx: number) => {
const address: any = {
provinceCode: null,
province: null,
cityCode: null,
city: null,
areaCode: null,
area: null,
}
if (value[idx].provinceCode && value[idx].cityCode) {
mutators.change([...value, address])
} else {
message.error(warningText)
}
}
// 删除一条地址
const removeAddress = (idx: any) => {
const requisitionFormAddressFilter = value.filter((item: any, index: number) => index !== idx)
const cityFilter = city.filter((item: any, index: number) => index !== idx)
// schema里面无法清空之前select数据
requisitionFormAddressFilter.forEach((item: any, index: number) => {
const cityCode = code.find(it => item.cityCode);
if (cityCode !== item.cityCode) {
item.disabled = false
}
})
setcity(cityFilter)
mutators.change(requisitionFormAddressFilter)
}
return (<RowStyleLayout>
{value.map((item: any, idx: number) => {
console.log(item, 'item')
return (
<Row gutter={10} key={`paramAddress${idx}_`} className="formwrap">
<Col span={8}>
<Form.Item>
<Select
onDropdownVisibleChange={onDropdownVisibleChange}
onChange={(value) => {
changeAddress(value, idx, 1)
}}
value={item.provinceCode}
>
{province.map(items => {
return (
<Option key={`${items.id}_${idx}_province`} value={items.code}>{items.name}</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item>
<Select
onDropdownVisibleChange={()=>onDropdownVisibleChange(2)}
onChange={(value) => {
changeAddress(value, idx, 2)
}}
value={item.cityCode}
>
{(item.provinceCode && city.length > 0 && city[idx]) && city[idx].citydata.map(items => {
return (
<Option disabled={items.disabled} key={`${items.id}_${idx}_city`} value={items.code}>{items.name}</Option>
)
})}
</Select>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item>
<Select
onDropdownVisibleChange={()=>onDropdownVisibleChange(3)}
onChange={(value) => {
changeAddress(value, idx, 3)
}}
value={item.areaCode}
>
{(item.provinceCode && area.length > 0 && area[idx]) && area[idx].areadata.map(items => {
return (
<Option disabled={items.disabled} key={`${items.id}_${idx}_area`} value={items.code}>{items.name}</Option>
)
})}
</Select>
</Form.Item>
</Col>
<div className="formbutton">
{
(idx === value.length - 1) &&
<Form.Item>
<Button style={{marginRight: 16}} type='primary' onClick={() => addAddress(idx)}><PlusOutlined /></Button>
</Form.Item>
}
{
value.length > 1 &&
<Form.Item>
<Button onClick={() => removeAddress(idx)}><MinusOutlined /></Button>
</Form.Item>
}
</div>
</Row>
)
})}
</RowStyleLayout>
)
}
MultAddress.defaultProps = {}
MultAddress.isFieldComponent = true;
export default MultAddress
......@@ -34,6 +34,7 @@ import SliderValidate from './components/SliderValidate';
import AntUpload from './components/AntUpload';
import { useLinkComponentProps } from './linkages/linkComponentProps';
import Loading from '../Loading';
import MultAddress from './components/MultAddress';
export interface NiceFormProps extends IAntdSchemaFormProps {
loading?: boolean
......@@ -111,6 +112,7 @@ export const componentExport = {
SliderValidate,
RadioGroup: Radio.Group,
AntUpload,
MultAddress,
}
const NiceForm: React.FC<NiceFormProps> = props => {
const { children, components, effects, expressionScope, loading = false, ...reset } = props;
......
......@@ -2,3 +2,6 @@ import { createContext } from 'react';
// 招标详情 Context
export const BidDetailContext = createContext<any>({})
// 待新增招标 详情
export const ReadyAddBidDetailContext = createContext<any>({})
import React, { useRef, useState, useEffect, useContext } from 'react'
import { history } from 'umi'
import { PageHeaderWrapper } from '@ant-design/pro-layout'
import ReutrnEle from '@/components/ReturnEle'
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus'
import { Button, Card, Row, Col, Drawer, message, Tooltip } from 'antd'
import { createFormActions, registerVirtualBox, useFormSpy, createAsyncFormActions } from '@formily/antd'
import { SaveOutlined, LinkOutlined, PlusOutlined, CodeSandboxCircleFilled, QuestionCircleOutlined, FilePdfOutlined, FilePdfFilled } from '@ant-design/icons'
import NiceForm from '@/components/NiceForm'
import moment from 'moment'
import styled from 'styled-components'
import { PublicApi } from '@/services/api'
import { formatTimeString, omit, findLastIndexFlowState } from '@/utils'
import styles from './index.less'
import { ReadyAddBidDetailContext } from '../../_public/bid/context'
import { mergeAllSchemas } from './schema'
export interface AddNewBidProps {}
const help = (title, desc) => {
return (
<div>
<span>{title}</span>
<Tooltip title={desc}>
<QuestionCircleOutlined
style={{ margin: "0 3px", cursor: "default", marginLeft: 3 }}
/>
</Tooltip>
</div>
);
};
const addSchemaAction = createFormActions()
// 采购订单详情页. 包含新增和编辑
const AddNewBid:React.FC<AddNewBidProps> = (props) => {
const shopDataRef = useRef<any>({})
const memberRef = useRef<any>({})
const inquiryRef = useRef<any>({})
const demandRef = useRef<any>({})
const contractRef = useRef<any>({})
const mergeRef = useRef<any>({})
const [formLoading, setFormLoading] = useState(false)
const [btnLoading, setBtnLoading] = useState(false)
const [productSumPrice, setProductSumPrice] = useState<number>(0)
const { pageStatus, id, page_type = '0', modelType, spam_id, lastTypeParams } = usePageStatus()
const [initFormValue, setInitFormValue] = useState<any>(() => {
let resultState = {}
if (modelType) {
resultState = {
orderModel: parseInt(modelType)
}
}
// 订单数据
if (spam_id) {
const item = JSON.parse(window.sessionStorage.getItem(spam_id))
if (item) {
console.log(item)
}
}
return resultState
})
useEffect(() => {
if (id) {
setFormLoading(true)
PublicApi.getOrderProcurementOrderDetails({
id
}).then(res => {
const { data } = res
setInitFormValue({
...data,
deliveryTime: formatTimeString(data.deliveryTime)
})
setFormLoading(false)
})
}
if (modelType) {
shopDataRef.current.orderModel = parseInt(modelType)
}
if (spam_id) {
const item = JSON.parse(window.sessionStorage.getItem(spam_id))
if (item) {
shopDataRef.current = Object.assign({}, shopDataRef.current, item)
}
}
}, [])
const handleSubmit = async (value) => {
console.log(value)
}
const providerValue = {
// detailData: initFormValue,
schemaActions: addSchemaAction,
}
return (
<PageHeaderWrapper
style={{margin: 0}}
onBack={() => history.goBack()}
backIcon={<ReutrnEle description="返回"/>}
title="新建招标"
extra={[
<Button key="1" onClick={() => addSchemaAction.submit()} loading={btnLoading} type="primary" icon={<SaveOutlined />}>
保存
</Button>,
]}
>
<ReadyAddBidDetailContext.Provider value={providerValue}>
<Card>
<NiceForm
loading={formLoading}
previewPlaceholder=' '
// editable={pageStatus !== PageStatus.PREVIEW}
value={initFormValue}
actions={addSchemaAction}
schema={mergeAllSchemas[page_type]}
onSubmit={handleSubmit}
components={{
}}
effects={($, ctx) => {
$('onFormMount').subscribe(() => {
})
}}
expressionScope={{
help,
}}
/>
</Card>
</ReadyAddBidDetailContext.Provider>
</PageHeaderWrapper>
)
}
AddNewBid.defaultProps = {}
export default AddNewBid
This diff is collapsed.
import React from 'react'
import { formatTimeString } from '@/utils'
import EyePreview from '@/components/EyePreview'
import { PlayCircleOutlined, PoweroffOutlined } from '@ant-design/icons'
import CustomTag from '@/pages/procurement/components/CustomTag'
import CustomBadge from '@/pages/procurement/components/customBadge'
export const readyAddBidListColumns: any[] = [
{
title: '招标编号/项目',
align: 'center',
dataIndex: 'orderNo',
key: 'orderNo',
render: (text, record) => <>
<EyePreview url={`/memberCenter/procurementAbility/callForBids/callForBidsSearch/preview?id=${record.id}`}>
{text}
</EyePreview>
<div>{text}</div>
</>
},
{
title: '采购类型',
align: 'center',
dataIndex: 'type',
key: 'type',
},
{
title: '招标方式',
align: 'left',
dataIndex: 'supplyMembersName',
key: 'supplyMembersName',
},
{
title: '发布时间',
align: 'center',
dataIndex: 'createTime',
key: 'createTime',
render: (text, record) => formatTimeString(record.createTime),
width: 200
},
{
title: '投标开始/截止时间',
align: 'center',
dataIndex: 'createTime',
key: 'createTime',
render: (text, record) => <>
<div><PlayCircleOutlined />{formatTimeString(record.createTime)}</div>
<div><PoweroffOutlined />{formatTimeString(record.createTime)}</div>
</>,
width: 200
},
{
title: '外部状态',
align: 'center',
dataIndex: 'externalState',
key: 'externalState',
render: text => <CustomTag status={text} type='out' />
},
{
title: '内部状态',
align: 'center',
dataIndex: 'interiorState',
key: 'interiorState',
render: (text) => <CustomBadge status={text} type='inside' />
},
]
import React from 'react'
import { history } from 'umi'
import { Card, Button, Space, Dropdown, Menu, 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'
import Submit from '@/components/NiceForm/components/Submit'
import { DatePicker } from '@formily/antd-components'
import { useSelfTable } from './model'
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable'
import { PurchaseOrderInsideWorkState } from '@/constants'
import { useHttpRequest } from '@/hooks/useHttpRequest'
import DateRangePickerUnix from '@/components/NiceForm/components/DateRangePickerUnix'
// 待新增订单
export interface ReadyAddOrderProps {}
const fetchTableData = async (params) => {
const { data } = await PublicApi.getOrderProcurementStayAddList(params)
return data
}
// TODO
const ReadyAddOrder:React.FC<ReadyAddOrderProps> = (props) => {
const { run: deleteRun } = useHttpRequest(PublicApi.postOrderProcurementOrderDeleteAll)
const { loading: submitLoading, run: submitRun } = useHttpRequest(PublicApi.postOrderProcurementOrderSubmitExamineAll)
const { columns, ref, rowSelection, rowSelectionCtl } = useSelfTable()
const handleMenuClick = async (e) => {
switch(e.key) {
case '1': {
// 批量删除
const canDelete = !rowSelectionCtl.selectRow.some(v => v.interiorState !== PurchaseOrderInsideWorkState.ADD_PURCHASE_ORDER)
if (canDelete) {
const { code } = await deleteRun({ids: rowSelectionCtl.selectedRowKeys})
if (code === 1000) {
ref.current.reload()
rowSelectionCtl.setSelectRow([])
rowSelectionCtl.setSelectedRowKeys([])
}
} else {
message.error('只能删除内部状态为未审核过的订单')
}
break;
}
}
}
const handleBitchPush = async () => {
const canBitch = !rowSelectionCtl.selectRow.some(v => v.interiorState !== PurchaseOrderInsideWorkState.ADD_PURCHASE_ORDER)
if (canBitch) {
const { code } = await submitRun({ids: rowSelectionCtl.selectedRowKeys})
if (code === 1000) {
ref.current.reload()
rowSelectionCtl.setSelectRow([])
rowSelectionCtl.setSelectedRowKeys([])
}
} else {
message.error('只能提交审核待新增的订单')
}
}
return <PageHeaderWrapper>
<Card>
<StandardTable
fetchTableData={params => fetchTableData(params)}
rowSelection={rowSelection}
columns={columns}
currentRef={ref}
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: 24
}
}}
formilyChilds={{
children: <Space>
<Button
icon={<PlusCircleOutlined/>}
type='primary'
onClick={() => history.push('/memberCenter/procurementAbility/callForBids/readyAddBid/add')}
>
新建
</Button>
<Button onClick={handleBitchPush} loading={submitLoading}>批量提交审核</Button>
<DropDeleteDown>
<Menu onClick={(e) => handleMenuClick(e)}>
<Menu.Item key="1" icon={<DeleteOutlined />}>
批量删除
</Menu.Item>
</Menu>
</DropDeleteDown>
</Space>,
layouts: {
span: 8
}
}}
/>
</Card>
</PageHeaderWrapper>
}
ReadyAddOrder.defaultProps = {}
export default ReadyAddOrder
import React, { useRef } from 'react'
import { Button, Popconfirm } from 'antd'
import { readyAddBidListColumns } from '../constant'
import { PublicApi } from '@/services/api'
import { useRowSelectionTable } from '@/hooks/useRowSelectionTable'
import { history } from 'umi'
import { PurchaseOrderInsideWorkState, PurchaseOrderOutWorkState } from '@/constants'
// 待新增招标逻辑
export const useSelfTable = () => {
const ref = useRef<any>({})
const [rowSelection, rowSelectionCtl] = useRowSelectionTable({customKey: 'id', extendsSelection: {
getCheckboxProps: record => ({
// 不等于可提交审核的 都无法通过批量提交
disabled: record.interiorState !== PurchaseOrderInsideWorkState.ADD_PURCHASE_ORDER,
interiorState: record.interiorState,
})
}})
const handleSubmit = async (id) => {
// 从待新增直接传到一级审核, 状态写死, 默认传-1
await PublicApi.postOrderProcurementOrderSubmitExamine({id, state: -1})
ref.current.reload()
}
const handleDelete = async (id) => {
await PublicApi.postOrderProcurementOrderDelete({id})
ref.current.reload()
}
const handleEdit = (record: any) => {
history.push(`/memberCenter/tranactionAbility/purchaseOrder/readyAddOrder/edit?id=${record.id}${record.type === 4 ? '&type=4' : ''}`)
}
const handleCancel = async (id) => {
await PublicApi.postOrderPurchaseOrderCancel({id})
ref.current.reload()
}
const secondColumns: any[] = readyAddBidListColumns.concat([
{
title: '操作',
align: 'center',
dataIndex: 'ctl',
key: 'ctl',
width: 200,
render: (text, record) => {
return <>
<Button type='link' onClick={() => handleSubmit(record.id)}>提交</Button>
<Button type='link' onClick={() => handleEdit(record)}>修改</Button>
<Popconfirm title='是否要删除该订单' onConfirm={() => handleDelete(record.id)}>
<Button type='link'>删除</Button>
</Popconfirm>
</>
}
}
])
return {
columns: secondColumns,
ref,
rowSelection,
rowSelectionCtl
}
}
import { ISchema } from '@formily/antd';
import { FORM_FILTER_PATH } from '@/formSchema/const';
/**
* 除了订单必填字段, 默认
*/
export const tableListSchema: ISchema = {
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: {
orderThe: {
type: 'string',
'x-component-props': {
placeholder: '请输入招标项目',
}
},
"[startCreateTime,endCreateTime]": {
type: 'array',
"x-component": 'DateRangePickerUnix',
'x-component-props': {
placeholder: ['发布开始时间','发布结束时间'],
},
},
submit: {
'x-component': 'Submit',
'x-component-props': {
children: '查询',
},
},
},
},
}
}
......@@ -6,6 +6,7 @@ import DescriptionsInfo from '../descriptionsInfo'
import BidMaterial from '../bidMaterial'
import BidMethod from '../bidMethod'
import BidTransformRecord from '../transferRecord'
import BidParticulars from '../bidParticulars'
export interface BidDetailSectionProps {
formContext: any,
......@@ -68,6 +69,14 @@ const BidDetailSection:React.FC<BidDetailSectionProps> = ({
<div>
<DescriptionsInfo cardTitle={anchorList[3]} type="bidNeed" />
</div>
{/* 调试结果 */}
<div>
<DescriptionsInfo cardTitle={anchorList[3]} type="bidResult" />
</div>
<div>
<BidParticulars cardTitle={anchorList[3]} />
</div>
{/* 调试结果 结束 */}
<div>
<DescriptionsInfo cardTitle={anchorList[4]} type="registerNeed" />
</div>
......
......@@ -18,7 +18,6 @@
height: 100%;
&:after {
content: "";
content: "";
height: 100%;
width: 1px;
background-color: #F4F5F7;
......@@ -61,11 +60,19 @@
font-size: 14px;
font-weight: 500;
color: #909399;
&:before {
content: "";
border: 2px solid #00B37A;
height: 14px;
line-height: 14px;
.longString {
color: #00B37A;
margin-right: 6px;
height: 14px;
line-height: 14px;
}
// &:before {
// content: "";
// border: 2px solid #00B37A;
// margin-right: 6px;
// }
}
}
}
......
.particulars {
margin-top: 24px;
:global {
.ant-table-tbody > tr.ant-table-row:hover > td {
border-top: 1px solid #00B37A;
border-bottom: 1px solid #00B37A;
background: #fff;
&:first-child {
border-left: 1px solid #00B37A;
}
&:last-child {
border-right: 1px solid #00B37A;
}
}
.ant-table-expanded-row td {
padding: 0;
}
}
}
.childrenWrap {
background-color: #fff;
.childrenTitle {
height: 100%;
background: linear-gradient(to left, #FFFFFF, #DAF2E7);
color: #00B37A;
p {
padding-left: 16px;
margin: 0;
font-size: 12px;
height: 14px;
line-height: 14px;
}
padding: 12px 0;
}
.childrenContent {
margin-top: 8px;
p {
margin: 0;
span {
height: 12px;
font-size: 12px;
font-weight: 400;
color: #909399;
line-height: 12px;
padding-right: 10px;
}
}
}
}
import React, { useContext, useEffect, useRef, useState } from 'react'
import { Table, Drawer, Button, Tabs, Row, Col } from 'antd'
import MellowCard from '@/components/MellowCard'
import { BidDetailContext } from '@/pages/procurement/_public/bid/context';
import EyePreview from '@/components/EyePreview';
import style from './index.less'
import { CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons';
/**
* 中标明细和投标物料嵌套表格
*/
export interface BidParticularsProps {
cardTitle?: string;
}
const BidParticulars: React.FC<BidParticularsProps> = ({cardTitle}) => {
const bidDetailContext = useContext(BidDetailContext)
const { data, ctl } = bidDetailContext
const columns = [
{ title: '物料编号/名称', dataIndex: 'name', key: 'name' },
{ title: '规格型号', dataIndex: 'age', key: 'age' },
{ title: '品类', dataIndex: 'address', key: 'address' },
{ title: '品牌', dataIndex: 'brand', key: 'brand' },
{ title: '采购数量/单位', dataIndex: 'number', key: 'number' },
{ title: '含税/税率', dataIndex: 'tax', key: 'tax' },
{ title: '单价(含税)', dataIndex: 'price', key: 'price' },
{ title: <span>金额(含税)<br />合计: ¥152,000.00</span>, dataIndex: 'amount', key: 'amount' },
];
const dataSource = [
{
key: 1,
name: <span>Q89YTE1<br />进口头层牛皮荔枝纹</span>,
age: '红色/XL/厚1.5mm',
address: '牛皮',
brand: 'PELLE',
number: <span>2000<br /></span>,
tax: <span><br />6%</span>,
price: "¥18.00",
amount: "¥38,000.00",
description: <div className={style.childrenWrap}>
<Row>
<Col span={3}>
<div className={style.childrenTitle}>
<p>对应</p>
<p>招标商品</p>
</div>
</Col>
<Col span={6}>
<div className={style.childrenContent}>
<p><span>商品编号:</span>Q89YTE1</p>
<p><span>商品名称:</span>进口头层黄牛皮荔枝纹</p>
</div>
</Col>
<Col span={6}>
<div className={style.childrenContent}>
<p><span>规格型号:</span>Q89YTE1</p>
<p><span>品类:</span>进口头层黄牛皮荔枝纹</p>
</div>
</Col>
<Col span={6}>
<div className={style.childrenContent}>
<p><span>品牌:</span>Q89YTE1</p>
</div>
</Col>
<Col span={3}>
<div className={style.childrenContent}>
<p><a href="">查看</a></p>
</div>
</Col>
</Row>
</div>,
},
{
key: 2,
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
description: 'My name is Jim Green, I am 42 years old, living in London No. 1 Lake Park.',
},
{
key: 3,
name: 'Not Expandable',
age: 29,
address: 'Jiangsu No. 1 Lake Park',
description: 'This not expandable',
},
{
key: 4,
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
description: 'My name is Joe Black, I am 32 years old, living in Sidney No. 1 Lake Park.',
},
];
return (<>
<MellowCard
title={cardTitle}
bordered={false}
fullHeight
className={style.particulars}
>
<Table
columns={columns}
expandable={{
expandedRowRender: record => <p style={{ margin: 0 }}>{record.description}</p>,
rowExpandable: record => record.name !== 'Not Expandable',
expandIcon: ({ expanded, onExpand, record }) =>
expanded ? (
<CaretDownOutlined onClick={e => onExpand(record, e)} />
) : (
<CaretRightOutlined onClick={e => onExpand(record, e)} />
)
}}
dataSource={dataSource}
pagination={{size: "small"}}
/>
</MellowCard>
</>)
}
BidParticulars.defaultProps = {}
export default BidParticulars
......@@ -7,3 +7,70 @@
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('../../../../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;
}
}
......@@ -4,7 +4,15 @@ import MellowCard from '@/components/MellowCard'
import { BidDetailContext } from '@/pages/procurement/_public/bid/context';
import { formatTimeString } from '@/utils'
import style from './index.less'
import { CaretDownOutlined, CaretUpOutlined, FileFilled } from '@ant-design/icons';
import { CaretDownOutlined, CaretUpOutlined, ExclamationCircleFilled, FileFilled } from '@ant-design/icons';
import {
Chart,
Coordinate,
Legend,
View,
} from 'bizcharts';
import Interval from 'bizcharts/lib/geometry/Interval'
import DataSet from "@antv/data-set";
/**
* 描述信息列表
......@@ -15,14 +23,16 @@ export interface BasicInfoProps {
cardTitle?: string;
/** 显示信息类型
* 'basicInfo' 基本信息 | 'bidNeed' 招标要求 | 'registerNeed' 报名要求 | 'checkNeed' 资格预审要求 | 'remarkNeed' 评标要求 | 'otherNeed' 其他要求
* 'bidResult' 中标结果
*/
type?: 'basicInfo' | 'bidNeed' | 'registerNeed' | 'checkNeed' | 'remarkNeed' | 'otherNeed';
type?: 'basicInfo' | 'bidNeed' | 'registerNeed' | 'checkNeed' | 'remarkNeed' | 'otherNeed' | 'bidResult';
}
const DescriptionsInfo: React.FC<BasicInfoProps> = ({cardTitle, type}) => {
const bidDetailContext = useContext(BidDetailContext)
const { data, ctl } = bidDetailContext
const [showMore, setShowMore] = useState<boolean>(false)
const [previewThank, setPreviewThank] = useState<boolean>(false)
const toogleMore = () => {
setShowMore(!showMore)
......@@ -216,6 +226,100 @@ const DescriptionsInfo: React.FC<BasicInfoProps> = ({cardTitle, type}) => {
},
]
/** 投标区块 **/
// 中标结果
const result = 0
const bidResultList = [
{
span: 8,
fieldList: [
{
title: '中标金额:',
noTitle: true,
name: 'createTime',
render: (text, record) => <Row justify='space-between'>
<Col style={{margin: "0 16px"}}>
<CircleChart sumPrice={record.sumPrice} alreadyPay={record.alreadyPay} />
</Col>
<Col>
<div><span className={style['card-list_title']}>中标金额(含税):</span></div>
<div><span>{record.alreadyPay || 0}</span></div>
</Col>
</Row>,
},
{ title: '中标理由:', name: 'createMemberName' },
]
},
{
span: 8,
fieldList: [
{ title: '中标公示:', name: 'createMemberName' },
{
title: '中标公示附件:',
name: 'paymentInformationResponses',
render: (t, r) => (<div>
{
data['paymentInformationResponses'].map((_item, _i) => <p><a key={_i}><FileFilled /> {_item.payNode}</a></p>)
}
</div>)
},
]
},
{
span: 8,
fieldList: [
{ title: '中标通知:', name: 'createMemberName' },
{
title: '中标通知附件:',
name: 'paymentInformationResponses',
render: (t, r) => (<div>
{
data['paymentInformationResponses'].map((_item, _i) => <p><a key={_i}><FileFilled /> {_item.payNode}</a></p>)
}
</div>)
},
]
},
]
const bidFail = [
{
span: 12,
fieldList: [
{
title: "中标失败",
name: 'createMemberName',
noTitle: true,
render: (t, r) => (<div>
<p className={style.resultFail}>
<ExclamationCircleFilled style={{fontSize: 24, paddingRight: 8, color:'#909399'}} />贵公司此次未中标!
</p>
<p className={style.resultFailSubtitle}>
非常感谢贵公司的积极参与,希望下次合作成功!
</p>
</div>)
},
]
},
{
span: 12,
fieldList: [
{
title: "查看感谢",
name: 'createMemberName',
noTitle: true,
rowStyle: {
justifyContent: 'flex-end',
},
render: (t, r) => (<div>
<p>
<a onClick={()=>setPreviewThank(true)}>查看感谢函</a>
</p>
</div>)
},
]
}
]
/** 类型数据映射 */
const Type_Data_Map = {
'basicInfo': basicColumnList,
......@@ -224,6 +328,41 @@ const DescriptionsInfo: React.FC<BasicInfoProps> = ({cardTitle, type}) => {
'checkNeed': checkNeedList,
'remarkNeed': remarkNeedList,
'otherNeed': otherNeedList,
// 投标区块
'bidResult': result ? bidResultList : bidFail,
}
// 圆形环状金额显示
const CircleChart = props => {
const { sumPrice = 100, alreadyPay = 10 } = props
const { DataView } = DataSet;
const userData = [
{ type: '总金额', value: (sumPrice - alreadyPay) || 100 },
{ type: '已支付', value: alreadyPay }
];
const userDv = new DataView();
userDv.source(userData).transform({
type: 'percent',
field: 'value',
dimension: 'type',
as: 'percent',
});
return <Chart placeholder={false} height={64} width={64} style={{position: "relative"}} autoFit>
<Legend visible={false} />
{/* 绘制图形 */}
<View data={userDv.rows}>
<Coordinate type="theta" innerRadius={0.75} />
<Interval
position="percent"
adjust="stack"
color={['type', ['#ffc400', '#6c9ceb']]}
tooltip={false}
/>
<p style={{position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)"}}>80%</p>
</View>
</Chart>
}
......@@ -231,8 +370,8 @@ const DescriptionsInfo: React.FC<BasicInfoProps> = ({cardTitle, type}) => {
{
infoList.map(({span, fieldList = []}, index) => (<Col key={index} span={span}>
{
fieldList.length ? fieldList.map((_v, _i) => <Row key={_v.name} className={style['card-list']}>
<Col span={6} className={style['card-list_title']}>{_v.title}</Col>
fieldList.length ? fieldList.map((_v, _i) => <Row key={_v.name} className={style['card-list']} style={_v.rowStyle}>
{_v?.noTitle ? null : <Col span={6} className={style['card-list_title']}>{_v.title}</Col>}
<Col>{_v.render ? _v.render(dataSource[_v.name], dataSource) : dataSource[_v.name]}</Col>
</Row>) : null
}
......@@ -279,6 +418,23 @@ const DescriptionsInfo: React.FC<BasicInfoProps> = ({cardTitle, type}) => {
</div>
: null
}
<Modal
title="感谢函"
visible={previewThank}
onOk={()=>setPreviewThank(false)}
onCancel={()=>setPreviewThank(false)}
width={660}
className={style.thankModal}
>
<div className={style.thankLetter}>
<h2>感谢函</h2>
<h4>THANKS LETTER</h4>
<p className={style.name}>尊敬的广州白马皮具交易中心</p>
<p>贵公司参与了我公司《进口头层黄牛皮荔枝纹采购》竞标。在我公司综合各投标单位的基本情况,并进行充分技术交流后,经评标委员会综合评定,贵公司未能中标。我公司对贵公司的积极参与和支持深表感谢!希望下次合作成功。</p>
<p className={style.company}>温州龙昌手袋有限公司</p>
<p className={style.time}>2020-08-25</p>
</div>
</Modal>
</MellowCard>)
}
......
......@@ -26,7 +26,6 @@ const fetchTableData = async (params) => {
return data
}
// TODO
const ReadyAddOrder:React.FC<ReadyAddOrderProps> = (props) => {
const { run: deleteRun } = useHttpRequest(PublicApi.postOrderProcurementOrderDeleteAll)
const { loading: submitLoading, run: submitRun } = useHttpRequest(PublicApi.postOrderProcurementOrderSubmitExamineAll)
......@@ -103,7 +102,6 @@ const ReadyAddOrder:React.FC<ReadyAddOrderProps> = (props) => {
<Button
icon={<PlusCircleOutlined/>}
type='primary'
// onClick={() => history.push('/memberCenter/tranactionAbility/purchaseOrder/orderDetail')}
onClick={() => history.push('/memberCenter/tranactionAbility/purchaseOrder/readyAddOrder/add')}
>
新建
......
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