Commit 7f44678e authored by 前端-钟卫鹏's avatar 前端-钟卫鹏

fix: 锚点抽屉组件嵌入NiceForm表单

parent c5714a56
...@@ -8,6 +8,22 @@ ...@@ -8,6 +8,22 @@
color: #909399; color: #909399;
} }
.paneTitle {
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 React, { ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react'
import { Modal, Drawer, Button, Tabs, Row, Col, Anchor } from 'antd' import { Modal, Drawer, Button, Tabs, Row, Col, Anchor } from 'antd'
import style from './index.less' import style from './index.less'
import NiceForm from '../NiceForm'
import { IAntdSchemaFormProps, registerVirtualBox } from '@formily/antd'
/** /**
* 带锚点定位跳转的抽屉 Drawer * 带锚点定位跳转的抽屉 Drawer
...@@ -8,57 +10,80 @@ import style from './index.less' ...@@ -8,57 +10,80 @@ import style from './index.less'
const { Link } = Anchor const { Link } = Anchor
export interface AnchorProps { export interface AnchorProps extends IAntdSchemaFormProps {
/** 源数据 */ /** 数据源 用于渲染对应的字段值 */
data?: any, data?: any,
/** 数据项和render渲染函数 */ /** 数据项和render渲染函数 仅预览功能下使用 */
dataList?: { title: string, name: string, render?(data?), [key: string]: any }[], dataRenderList?: { title: string, name: string, render?(data?: any), [key: string]: any }[],
/** NiceForm表单模式下 锚点定位title和idName列表 此项生效必须isForm属性为true */
dataIdList?: {title: string, idName: string}[],
title?: string, title?: string,
visible?: boolean, visible?: boolean,
/** 自定义底部布局 */ /** 自定义底部布局 */
footer?: ReactNode, footer?: ReactNode,
/** 点击遮罩回调 */ /** 点击遮罩回调 */
onClose?: any, onClose?: any,
/** 是否使用NiceForm表单 */
isForm?: boolean,
} }
const AnchorDrawer: React.FC<AnchorProps> = ({ const AnchorDrawer: React.FC<AnchorProps> = ({
data, data,
dataList, dataRenderList = [],
dataIdList = [],
title = "", title = "",
visible = false, visible = false,
footer, footer,
onClose, onClose,
isForm = false,
actions,
...restProps ...restProps
}) => { }) => {
const [current, setCurrent] = useState<number>(0) const [current, setCurrent] = useState<number>(0)
const [offsetTopList, setOffsetTopList] = useState<number[]>([]) const [offsetTopList, setOffsetTopList] = useState<number[]>([])
// useEffect(() => {
// if (currentRef) {
// currentRef.current = {
// formActions,
// }
// }
// }, [])
useEffect(() => { useEffect(() => {
let tempArr: any = []
let floors: any = []
if(visible) { if(visible) {
// 开启滚动事件冒泡
window.addEventListener("scroll", onScroll, true);
// 获取各个子div距父级的高度 // 获取各个子div距父级的高度
let floors = document.querySelectorAll(".drawerContent>div") floors = isForm ? document.querySelectorAll("form.ant-form>div") : document.querySelectorAll(".drawerContent>div")
let tempArr = []
floors.forEach((floor: any, index: any) => { floors.forEach((floor: any, index: any) => {
tempArr.push(floor.offsetTop) tempArr.push(floor.offsetTop)
}) })
setOffsetTopList(tempArr) setOffsetTopList(tempArr)
} }
/**
* 开启滚动事件冒泡 (传参说明)
* e: 事件对象
* tempArr: 各目标元素距父级顶部距离数组
* floors: 各锚点目标HTML元素
*/
window.addEventListener("scroll", (e?: any) => onScroll(e, tempArr, floors), true);
// @ts-ignore
return () => window.removeEventListener('scroll', onScroll) return () => window.removeEventListener('scroll', onScroll)
}, [visible]) }, [visible])
const onScroll = (e) => { const onScroll = (e?: { target: { className: string } }, arr?: any, floors?: any) => {
if(e.target.className === 'ant-drawer-body') { if(visible)
let scrollTop = document.querySelectorAll(".ant-drawer-body")[0].scrollTop if(e.target.className === 'ant-drawer-body') {
let floors = document.querySelectorAll(".drawerContent>div") let scrollTop = document.querySelectorAll(".ant-drawer-body")[0].scrollTop
floors.forEach((floor: any, index: any) => { floors.forEach((floor: any, index: any) => {
if(floor.offsetTop - 40 <= scrollTop) { if(arr[index] - 40 <= scrollTop) {
setCurrent(index) setCurrent(index)
} }
}) })
} }
} }
const handleClick = (i: number) => { const handleClick = (i: number) => {
...@@ -72,6 +97,18 @@ const AnchorDrawer: React.FC<AnchorProps> = ({ ...@@ -72,6 +97,18 @@ const AnchorDrawer: React.FC<AnchorProps> = ({
} }
} }
registerVirtualBox("CustomTitle", ({ children, schema }) => {
return (
<div>
<p className={style.paneTitle}>
<span className={style.longString}>|</span>
{schema["x-component-props"]["text"]}
</p>
{children}
</div>
);
});
return (<div id="content"> return (<div id="content">
<Drawer <Drawer
title={title} title={title}
...@@ -88,24 +125,26 @@ const AnchorDrawer: React.FC<AnchorProps> = ({ ...@@ -88,24 +125,26 @@ const AnchorDrawer: React.FC<AnchorProps> = ({
<div className={style.drawerNav}> <div className={style.drawerNav}>
<ul> <ul>
{ {
dataList.map((item, index) => ( isForm
?
dataIdList.map((item, index) => (
<li key={index} onClick={() => handleClick(index)}><a className={current === index ? style.current : null}>{item['title']}</a></li>
)) :
dataRenderList.map((item, index) => (
<li key={index} onClick={() => handleClick(index)}><a className={current === index ? style.current : null}>{item['title']}</a></li> <li key={index} onClick={() => handleClick(index)}><a className={current === index ? style.current : null}>{item['title']}</a></li>
)) ))
} }
</ul> </ul>
{/* <Anchor onClick={(e)=>e.preventDefault()} showInkInFixed={false} getContainer={() => document.getElementById("content")}>
{
dataList.map((item, index) => (
<Link href={`#${item['id']}`} title={item['title']} />
))
}
</Anchor> */}
</div> </div>
</Col> </Col>
<Col span={18}> <Col span={18}>
<div className={[style.drawerContent, 'drawerContent'].join(' ')}> <div className={[style.drawerContent, 'drawerContent'].join(' ')}>
{ {
dataList.map((item, index) => ( isForm ? (<NiceForm
actions={actions}
{...restProps}
// onSubmit={values => ref.current.reload(values)}
/>) : (dataRenderList.map((item, index) => (
<div className={style.drawerContentItem} id={item['id']} key={index}> <div className={style.drawerContentItem} id={item['id']} key={index}>
<div className={style.drawerContentTitle}> <div className={style.drawerContentTitle}>
<p><span className={style.longString}>|</span>{item['title']}</p> <p><span className={style.longString}>|</span>{item['title']}</p>
...@@ -114,7 +153,7 @@ const AnchorDrawer: React.FC<AnchorProps> = ({ ...@@ -114,7 +153,7 @@ const AnchorDrawer: React.FC<AnchorProps> = ({
{item.render ? item.render(data) : <span>{data[item.name]}</span>} {item.render ? item.render(data) : <span>{data[item.name]}</span>}
</div> </div>
</div> </div>
)) )))
} }
</div> </div>
</Col> </Col>
......
...@@ -359,6 +359,11 @@ a { ...@@ -359,6 +359,11 @@ a {
} }
} }
// 重置步骤条顶部边距
.ant-steps-horizontal {
margin-top: 0 !important;
}
// 固定头部 // 固定头部
// .ant-layout-header { // .ant-layout-header {
// position: fixed; // position: fixed;
......
...@@ -637,5 +637,8 @@ export default { ...@@ -637,5 +637,8 @@ export default {
// 采购能力 // 采购能力
'menu.procurementAbility': '采购能力', 'menu.procurementAbility': '采购能力',
'menu.procurementAbility.callForBids': '招标', 'menu.procurementAbility.callForBids': '招标',
'menu.procurementAbility.callForBids.callForBidsSearch': '招标查询',
'menu.procurementAbility.purchaseInquiry': '采购询价', 'menu.procurementAbility.purchaseInquiry': '采购询价',
}; };
import React, { useState, useCallback, useRef, useContext, useEffect, createRef } from 'react'; import React, { useState, useCallback, useRef, useContext, useEffect, createRef } from 'react';
import { history } from 'umi' import { history } from 'umi'
import { Button } from 'antd'; import { Button, Row, Col } from 'antd';
import style from './index.less' import style from './index.less'
import OrderDetailWrapper from '@/pages/transaction/components/OrderDetailWrapper'; import OrderDetailWrapper from '@/pages/transaction/components/OrderDetailWrapper';
import PreLoading from '@/components/PreLoading'; import PreLoading from '@/components/PreLoading';
...@@ -9,12 +9,16 @@ import { BidDetailContext } from '@/pages/procurement/_public/bid/context'; ...@@ -9,12 +9,16 @@ import { BidDetailContext } from '@/pages/procurement/_public/bid/context';
import { useBidDetail } from '@/pages/procurement/_public/bid/effects/useBidDetail'; import { useBidDetail } from '@/pages/procurement/_public/bid/effects/useBidDetail';
import BidDetailHeader from '@/pages/procurement/components/bidDetailHeader'; import BidDetailHeader from '@/pages/procurement/components/bidDetailHeader';
import BidDetailSection from '@/pages/procurement/components/bidDetailSection'; import BidDetailSection from '@/pages/procurement/components/bidDetailSection';
import AnchorDrawer from '@/components/AnchorDrawer';
import { createFormActions } from '@formily/antd';
const formActions = createFormActions();
const ReadySendBidNoticeDetail: React.FC = () => { const ReadySendBidNoticeDetail: React.FC = () => {
const { formContext, id, detailList } = useBidDetail({type: 'purchaseOrder'}) const { formContext, id, detailList } = useBidDetail({type: 'purchaseOrder'})
const {data, currentPayInfoId} = formContext const {data, currentPayInfoId} = formContext
const payRef = useRef<any>({}) const modalRef = useRef<any>({})
const [visible, setVisible] = useState<boolean>(false)
/** 约定好 对应的锚点title和元素id映射 */ /** 约定好 对应的锚点title和元素id映射 */
// type? 用于区分DescriptionsInfo组件的内容 // type? 用于区分DescriptionsInfo组件的内容
...@@ -26,6 +30,46 @@ const ReadySendBidNoticeDetail: React.FC = () => { ...@@ -26,6 +30,46 @@ const ReadySendBidNoticeDetail: React.FC = () => {
{ title: '流转记录', id: 'transferRecord', componentName: "BidTransformRecord" }, { title: '流转记录', id: 'transferRecord', componentName: "BidTransformRecord" },
] ]
const dataIdList = [
{
title: '中标公示',
idName: 'bidNotice',
},
{
title: '中标通知',
idName: 'bidMessage',
},
{
title: '感谢函',
idName: 'thankLetter',
}
]
const confirmSubmit = () => {
// setVisible(false)
formActions.validate().then((res) => {
console.log(res)
if(res['errors']['length'] === 0) {
formActions.submit(v => console.log(v))
}
})
}
const footer = (<div
style={{
textAlign: 'right',
}}
>
<Button onClick={()=>setVisible(false)} style={{ marginRight: 8 }}>
取消
</Button>
<Button onClick={confirmSubmit} type="primary">
确定
</Button>
</div>)
return ( return (
<div> <div>
<BidDetailContext.Provider value={formContext}> <BidDetailContext.Provider value={formContext}>
...@@ -35,7 +79,7 @@ const ReadySendBidNoticeDetail: React.FC = () => { ...@@ -35,7 +79,7 @@ const ReadySendBidNoticeDetail: React.FC = () => {
anchorList={anchorTitleList} anchorList={anchorTitleList}
backLink="/memberCenter/procurementAbility/callForBids/callForBidsSearch" backLink="/memberCenter/procurementAbility/callForBids/callForBidsSearch"
extraRight={ extraRight={
<Button type='primary'> <Button type='primary' onClick={() => setVisible(true)}>
发送中标公示 发送中标公示
</Button> </Button>
} }
...@@ -46,8 +90,146 @@ const ReadySendBidNoticeDetail: React.FC = () => { ...@@ -46,8 +90,146 @@ const ReadySendBidNoticeDetail: React.FC = () => {
</PreLoading> </PreLoading>
</OrderDetailWrapper> </OrderDetailWrapper>
<OrderPayModal <AnchorDrawer
currentRef={payRef} title="发送中标公示"
visible={visible}
dataIdList={dataIdList}
footer={footer}
onClose={() => setVisible(false)}
isForm = {true}
actions={formActions}
schema={{
type: 'object',
properties: {
Text_1: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "中标公示"
},
properties: {
NO_SUBMIT_LAYOUT_1: {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelAlign: "top",
full: true
},
properties:{
isSendNotice: {
type: 'string',
"x-component-props": {
children: "发送中标公示"
},
"x-component": "checkboxsingle",
default: true,
readOnly: true,
},
noticeText: {
type: 'textarea',
title: '',
required: true,
'x-component-props': {
placeholder: '请填写中标公示',
rows: 4
}
},
noticeFileList: {
title: '中标公示附件',
'x-component': 'AntUpload',
'x-component-props': {
action: '/api/file/file/upload/prefix',
data: {
fileType: 1,
prefix: '/afterService/returnApplication/',
},
// beforeUpload: '{{beforeUpload}}',
accept: '.xls, .xlsx, .doc, .docx, .wps, .pdf, .jpg, .png, .jpeg',
},
'x-rules': [
{
required: false,
message: '请上传附件',
},
],
// description: '一次上传一个文件,每个附件大小不能超过20M',
},
}
}
}
},
Text_2: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "中标通知"
},
properties: {
isSendMessage: {
type: 'string',
"x-component-props": {
children: "发送中标通知"
},
"x-component": "checkboxsingle",
default: false,
},
messageText: {
type: 'textarea',
title: '',
'x-component-props': {
placeholder: '请填写中标通知',
rows: 4
}
},
messageFileList: {
title: '中标通知附件',
'x-component': 'AntUpload',
'x-component-props': {
action: '/api/file/file/upload/prefix',
data: {
fileType: 1,
prefix: '/afterService/returnApplication/',
},
// beforeUpload: '{{beforeUpload}}',
accept: '.xls, .xlsx, .doc, .docx, .wps, .pdf, .jpg, .png, .jpeg',
},
'x-rules': [
{
required: false,
message: '请上传附件',
},
],
// description: '一次上传一个文件,每个附件大小不能超过20M',
},
}
},
Text_3: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "感谢信"
},
properties: {
isSendThankLetter: {
type: 'string',
"x-component-props": {
children: "发送感谢信"
},
"x-component": "checkboxsingle",
default: false,
},
thankLetterText: {
type: 'textarea',
title: '',
'x-component-props': {
placeholder: '请填写感谢信',
rows: 4
}
},
}
},
}
}}
/> />
</BidDetailContext.Provider> </BidDetailContext.Provider>
</div> </div>
......
...@@ -256,7 +256,7 @@ const BidMaterial: React.FC<BidMaterialProps> = ({cardTitle}) => { ...@@ -256,7 +256,7 @@ const BidMaterial: React.FC<BidMaterialProps> = ({cardTitle}) => {
<AnchorDrawer <AnchorDrawer
title="物料详情" title="物料详情"
visible={visible} visible={visible}
dataList={dataList} dataRenderList={dataList}
footer={footer} footer={footer}
onClose={() => setVisible(false)} onClose={() => setVisible(false)}
/> />
......
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