Commit 4c537d3d authored by 前端-钟卫鹏's avatar 前端-钟卫鹏

feat: 添加新增投标

parent e53eeeda
......@@ -56,7 +56,8 @@ const AnchorDrawer: React.FC<AnchorProps> = ({
let floors: any = []
if(visible) {
// 获取各个子div距父级的高度
floors = isForm ? document.querySelectorAll("form.ant-form>div") : document.querySelectorAll(".drawerContent>div")
floors = isForm ? document.querySelectorAll(".drawerContent>div>div>form.ant-form>div") : document.querySelectorAll(".drawerContent>div")
console.log(floors, 'fff')
floors.forEach((floor: any, index: any) => {
tempArr.push(floor.offsetTop)
})
......@@ -95,6 +96,7 @@ const AnchorDrawer: React.FC<AnchorProps> = ({
if(offsetTopList[i] + dom.clientHeight >= dom.scrollHeight) {
setCurrent(i)
}
console.log(offsetTopList, offsetTopList[i], dom.clientHeight, dom.scrollHeight)
}
registerVirtualBox("CustomTitle", ({ children, schema }) => {
......
......@@ -23,4 +23,4 @@ MultTable.defaultProps = {}
MultTable.isFieldComponent = true;
export default MultTable
\ No newline at end of file
export default MultTable
......@@ -102,6 +102,7 @@ const BidParticulars: React.FC<BidParticularsProps> = ({cardTitle}) => {
bordered={false}
fullHeight
className={style.particulars}
style={{marginTop: 24}}
>
<Table
columns={columns}
......
import React, { useRef, useContext } from 'react'
import { Form, Input } from 'antd';
export interface ProductTableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: string;
record: any;
handleSave: (record: any) => Promise<any>;
forceEdit: boolean,
formItem: string,
formItemProps: any,
}
const EditableContext = React.createContext<any>({});
export const ProductEditableRow: React.FC<any> = ({...props }) => {
const [form] = Form.useForm();
const ctx = {
form
}
return (
<Form form={form} component={false}>
<EditableContext.Provider value={ctx}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
export const ProductTableCell:React.FC<ProductTableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
forceEdit,
formItem,
formItemProps={},
...restProps
}) => {
const formItemRef = useRef<any>();
const { form } = useContext(EditableContext);
const save = async e => {
try {
const values = await form.validateFields();
values.tax = Number(values.tax) || 0
values.unitPrice = Number(values.unitPrice) || 0
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log('Save failed:', errInfo);
}
};
const chooseFormItem = (type) => {
switch(type) {
case 'input': {
return <Input
style={{width: 140}}
type='number'
ref={formItemRef}
onChange={save}
{...formItemProps}
id={dataIndex + record.id}
className="purchase_amount_input"
/>
}
}
}
let childNode = children;
if (editable) {
childNode =
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
initialValue={record[dataIndex] || ''}
rules={[
{
required: true,
message: `${title}必须填写`,
},
{
pattern: /^\d+(\.\d{1,2})?$/,
message: '采购数量仅限两位小数',
},
]}
>
{chooseFormItem(formItem)}
</Form.Item>
}
return <td {...restProps}>{childNode}</td>;
}
ProductTableCell.defaultProps = {}
export default ProductTableCell
.card-list {
font-size: 12px;
line-height: 20px;
padding: 8px;
// margin-top: 24px;
}
.card-list_title {
font-size: 12px;
color: #909399;
}
import React, { useRef, useState, useEffect } from 'react'
import { createFormActions, ISchemaFormActions, ISchemaFormAsyncActions, registerVirtualBox } from '@formily/antd'
import { Button, Row, Col } from 'antd'
import { useModalTable } from '../../model/useModalTable'
import AnchorDrawer from '@/components/AnchorDrawer'
import style from './index.less'
import { PlusOutlined } from '@ant-design/icons'
import DrawerTable from '@/components/DrawerTable'
import { FORM_FILTER_PATH } from '@/formSchema/const'
import { useStateFilterSearchLinkageEffect } from '@/formSchema/effects/useFilterSearch'
import Search from '@/components/NiceForm/components/Search'
import Submit from '@/components/NiceForm/components/Submit'
import { PublicApi } from '@/services/api'
import { productSearch, relevanceSchema } from '../../schema/modal'
export interface RelateProductDrawerProps {
type?: 'radio' | 'checkbox',
title?: string,
schemaAction: ISchemaFormActions | ISchemaFormAsyncActions,
currentRef?: any,
dataIdList: any,
confirmModal?()
}
const formActions = createFormActions();
export const RelevanceTenderProduct:React.FC<RelateProductDrawerProps> = ({
type = 'checkbox',
title,
schemaAction,
confirmModal,
currentRef,
dataIdList,
...restProps
}) => {
const { visible, setVisible } = useModalTable({type, customKey: 'id'})
const [childVisible, setChildVisible] = useState<boolean>(false)
const [selectRow, setSelectRow] = useState<any[]>([]) // 子级抽屉选择的行数据
const [selectedRowKeys, setSelectedRowKeys] = useState<Array<string>>([])
useEffect(() => {
if (currentRef) {
currentRef.current = {
setVisible,
visible,
}
}
}, [])
// 注册virtual组件 一般用于布局
registerVirtualBox("CustomLayout", ({props, schema}) => {
const {
showStar = false,
headerBackgroundColor,
headerColor,
whetherSelect = false,
} = schema["x-component-props"]
return (
<div style={{ border: "1px solid #DAF2E7", marginBottom: 16 }}>
<p style={{paddingLeft: 8, fontSize: 12, backgroundColor: headerBackgroundColor, color: headerColor}}>{props['title']}<span style={{color: 'red'}}>{showStar?'*':null}</span></p>
<div className={style['card-list']}>
{
whetherSelect ?
<Button type="dashed" block icon={<PlusOutlined />} onClick={selectProduct}>
选择商品
</Button>
:
<Row>
<Col span={12}>
<Row>
<Col span={8}><p className={style['card-list_title']}>招标方式</p></Col>
<Col><p>系统匹配</p></Col>
</Row>
</Col>
<Col span={12}>
<Row>
<Col span={8}><p className={style['card-list_title']}>招标方式</p></Col>
<Col><p>系统匹配</p></Col>
</Row>
</Col>
</Row>
}
</div>
</div>
);
});
const selectProduct = () => {
setChildVisible(true)
}
const columns: any[] = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: '属性名称',
dataIndex: 'name',
key: 'name',
},
{
title: '属性组名',
dataIndex: 'groupName',
key: 'groupName',
},
{
title: '展示方式',
dataIndex: 'type',
key: 'type',
render: (text: number) => {
let txt = new Map([[1, '单选'], [2, '多选'], [3, '输入']])
return txt.get(text)
}
},
{
title: '是否必填',
dataIndex: 'isEmpty',
key: 'isEmpty',
render: (text: boolean) => text ? '是' : '否'
}
]
// 父级抽屉提交
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>)
// 子级抽屉点击确定
const onConfirm = () => {
setChildVisible(false)
console.log('确定', setSelectRow)
}
const fetchData = (params: any) => {
return new Promise((resolve, reject) => {
//@ts-ignore
PublicApi.getProductPlatformGetAttributeList({ ...params, name: params.name || '', groupName: params.groupName || '', isEnable: true }).then(res => {
resolve(res.data)
})
})
}
const rowSelection: any = {
type: 'radio',
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys: any, selectedRows: any) => {
setSelectRow(selectedRows)
setSelectedRowKeys(selectedRowKeys)
}
}
return <AnchorDrawer
title={title}
visible={visible}
dataIdList={dataIdList}
footer={footer}
onClose={() => setVisible(false)}
isForm = {true}
actions={formActions}
// effects={($, ctx) => {
// $('onFormMount').subscribe(() => {
// ctx.setFieldValue('tenderProduct', {a: 1, b: 2, c: 'ds'})
// })
// }}
schema={relevanceSchema}
>
<DrawerTable
drawerTitle='选择商品'
confirm={onConfirm}
cancel={() => setChildVisible(false)}
visible={childVisible}
columns={columns}
rowSelection={rowSelection}
fetchTableData={(params: any) => fetchData(params)}
formilyProps={
{
ctx: {
schema: productSearch,
components: { ModalSearch: Search, Submit } ,
effects: ($, actions) => {
actions.reset()
useStateFilterSearchLinkageEffect(
$,
actions,
'groupName',
FORM_FILTER_PATH,
);
}
}
}
}
// resetDrawer={{
// destroyOnClose: true
// }}
tableProps={{
rowKey: 'id',
}}
/>
</AnchorDrawer>
}
RelevanceTenderProduct.defaultProps = {}
export default RelevanceTenderProduct
// 选择商品和会员弹框的列
export const columnsSetMember: any[] = [
{
title: 'ID',
dataIndex: 'memberId',
align: 'center',
key: 'memberId',
},
{
title: '会员名称',
dataIndex: 'name',
align: 'center',
key: 'name',
},
{
title: '会员类型',
dataIndex: 'memberTypeName',
align: 'center',
key: 'memberTypeName',
},
{
title: '会员角色',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '会员等级',
dataIndex: 'levelTag',
align: 'center',
key: 'levelTag',
}
]
import React from 'react'
import { formatTimeString } from '@/utils'
// 选择商品和会员弹框的列
export const columnsSetMember: any[] = [
{
title: 'ID',
dataIndex: 'memberId',
align: 'center',
key: 'memberId',
},
{
title: '会员名称',
dataIndex: 'name',
align: 'center',
key: 'name',
},
{
title: '会员类型',
dataIndex: 'memberTypeName',
align: 'center',
key: 'memberTypeName',
},
{
title: '会员角色',
dataIndex: 'roleName',
align: 'center',
key: 'roleName',
},
{
title: '会员等级',
dataIndex: 'levelTag',
align: 'center',
key: 'levelTag',
}
]
// 新增投标 投标商品列表
export const productInfoColumns: any[] = [
{
title: 'ID',
dataIndex: 'id',
align: 'center',
key: 'id',
className: 'commonHide',
},
{
title: '物料编号/名称',
dataIndex: 'name',
align: 'center',
key: 'name',
render: (t, r) => <><div>{t}</div><div>{t}</div></>
},
{
title: '品类',
dataIndex: 'age',
align: 'center',
key: 'age',
},
{
title: '品牌',
dataIndex: 'brand',
align: 'center',
key: 'brand',
},
{
title: '采购数量/单位',
dataIndex: 'unit',
align: 'center',
key: 'unit',
render: (t, r) => <><div>{r.purchaseCount}</div><div>{t}</div></>
},
{
title: '含税',
dataIndex: 'hasTax',
align: 'center',
key: 'hasTax',
},
{
title: '税率',
dataIndex: 'tax',
align: 'center',
key: 'tax',
formItem: 'input',
editable: true,
width: 140,
formItemProps: {
suffix: '%',
},
},
{
title: '单价(含税)',
dataIndex: 'unitPrice',
align: 'left',
key: 'unitPrice',
formItem: 'input',
editable: true,
width: 140,
formItemProps: {
prefix: '¥',
},
},
{
title: '金额(含税)',
dataIndex: 'money',
align: 'center',
key: 'money',
},
{
title: '操作',
dataIndex: 'ctl',
align: 'center',
key: 'ctl',
},
]
......@@ -33,3 +33,43 @@
color: #909399;
}
.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;
}
}
}
}
.restContainer {
margin-top: 24px;
:global {
.ant-table-expanded-row td {
padding: 0;
}
}
}
import React, { useRef, useMemo, useState } from 'react'
import { ISchemaFormActions, ISchemaFormAsyncActions, createControllerBox, useFormSpy } from '@formily/antd';
import { Button, Row, Col, message } from 'antd';
import { productInfoColumns } from '../constant';
import ProductTableCell, { ProductEditableRow } from '../components/productTableCell';
import { useModalTable } from './useModalTable';
import { usePageStatus, PageStatus } from '@/hooks/usePageStatus';
import { OrderModalType } from '@/constants';
import { history } from "umi";
import { CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons';
const { pageStatus } = usePageStatus()
// 对象按key排序(运用于商城传过来的阶梯价格排序)
export const sortByKey = (params) => {
let keys = Object.keys(params).sort((x,y)=> parseInt(x) - parseInt(y));
let newParams = {};
keys.forEach((key) => {
newParams[key] = params[key];
});
return newParams;
}
export const getUnitPriceTotal = (record) => {
const purchaseCount = Number(record['purchaseCount']) || 0
// fix 当没有传递unitPrice字段时 自动容错, 单价显示为0
// fix 编辑订单取price
record.unitPrice = pageStatus === PageStatus.EDIT ? record.price : record.unitPrice || record.price || 0
if (typeof record.unitPrice === 'number') {
return record.isMemberPrice ?
Number((record.unitPrice * purchaseCount * record.memberPrice).toFixed(2))
:
Number((record.unitPrice * purchaseCount).toFixed(2))
}
if(record.unitPrice) {
record.unitPrice = sortByKey(record.unitPrice)
}
// fix 当没有传递unitPrice字段时 但有price字段时 补全unitPrice字段
if(record.price && JSON.stringify(record.unitPrice) === "{}") {
record.unitPrice = {'0-0': record.price}
}
let unitPrice = 0
Object.entries(record.unitPrice).forEach(([key, value]) => {
const [min, max] = key.split('-').map(v => Number(v))
if (min === 0 && max === 0) {
unitPrice = Number(value)
return false
}
if ((purchaseCount >= min && purchaseCount <= max) || (purchaseCount > max)) {
// 处于该区间或者大于该区间
unitPrice = Number(value)
return false
}
})
// 考虑会员折扣
let memberPrice = record.memberPrice
if(record.isMemberPrice) {
return Number((unitPrice * purchaseCount * memberPrice).toFixed(2))
} else {
return Number((unitPrice * purchaseCount).toFixed(2))
}
}
/**
* @param ctx schemaAction
* @param relevanceRef 关联报价商品抽屉的ref
*/
export const useProductTable = (ctx: ISchemaFormActions | ISchemaFormAsyncActions, relevanceRef: any) => {
const clickMergeButton = (recrod) => {
relevanceRef.current.setVisible(true)
relevanceRef.current.setCurrentClickRow(recrod)
}
const clickRelevance = (r) => {
relevanceRef.current.setVisible(true)
}
const [productColumns, setProductColumns] = useState(() => {
if (pageStatus === PageStatus.ADD) {
// 渲染操作
productInfoColumns[productInfoColumns.length - 1].render = (text, record) => {
return <Button type='link' onClick={(r) => clickRelevance(r)}>关联投标商品</Button>
}
} else {
// // 渲染单价
// productInfoColumns[5].render = (t, r) => <span style={{color: 'red'}}>¥ {r.price}</span>
// // 渲染商品ID
// productInfoColumns[0].render = (t, r) => r.productId
// // 编辑并且为合并下单模式 显示合并按钮 url type===4判断类型
// if(pageStatus === PageStatus.EDIT && history.location.query?.type) {
// productInfoColumns[productInfoColumns.length - 1].render = (text, record) => <Button type='link' className="selectMerge" onClick={() => clickMergeButton(record)}>选择合并订单</Button>
// return [...productInfoColumns]
// } else {
// return [...productInfoColumns].slice(0, productInfoColumns.length - 1)
// }
}
return productInfoColumns
})
const productComponents = {
body: {
row: ProductEditableRow,
cell: ProductTableCell
}
}
const handleSave = row => {
return new Promise((resolve, reject) => {
const newData = [...ctx.getFieldValue('tenderProductRequests')];
const index = newData.findIndex(item => row.id === item.id);
const item = newData[index];
row['money'] = getUnitPriceTotal(row)
newData.splice(index, 1, {
...item,
...row,
});
ctx.setFieldValue('tenderProductRequests', newData)
// thead总计变动
let _ = [...productInfoColumns]
_[_.length - 2]['title'] = <>
<div>金额(含税)</div>
<div>总计:¥<span style={{fontWeight: 'bolder'}}>{newData.reduce((prev, next) => (prev*100 + (next.money || 0)*100)/100, 0)}</span></div>
</>
setProductColumns(_)
resolve({item, newData})
})
};
const productMergeColumns = productColumns.map(col => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: ctx.getFormState().editable === false ? false : col.editable,
dataIndex: col.dataIndex,
title: col.title,
formItem: col.formItem,
formItemProps: col.formItemProps,
handleSave
}),
};
})
// 嵌套子表格
const productChildren = {
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)} />
)
}
return {
productColumns: productMergeColumns,
productComponents,
productChildren,
}
}
......@@ -296,12 +296,21 @@ export const formSchema: ISchema = {
type: 'object',
"x-component": 'mega-layout',
"x-component-props": {
labelCol: 4,
wrapperCol: 8,
labelAlign: 'left'
// labelCol: 4,
// wrapperCol: 8,
// labelAlign: 'left'
},
properties: {
tenderProductRequests: {
type: 'array',
"x-component": 'MultTable',
"x-component-props": {
rowKey: 'id',
columns: "{{productColumns}}",
components: "{{productComponents}}",
expandable: "{{productChildren}}",
},
},
}
}
}
......
import { FORM_FILTER_PATH } from "@/formSchema/const";
import { ISchema } from "@formily/antd";
// 选择商品和会员高级筛选
export const formSearch: ISchema = {
// 关联投标商品schema
export const relevanceSchema: ISchema = {
type: 'object',
properties: {
name: {
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:{
bidMaterial: {
type: "object",
title: "对应招标物料",
"x-component": "CustomLayout",
"x-component-props": {
headerBackgroundColor: '#E4F7EF',
headerColor: '#00B37A',
}
},
tenderProduct: {
type: "object",
title: "投标商品",
"x-component": "CustomLayout",
"x-component-props": {
showStar: true,
headerBackgroundColor: '#F0F7FF',
headerColor: '#3877FF',
whetherSelect: true
},
// default: {a: 1, b: 25, c: 'er'},
}
}
}
}
},
Text_2: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "产地"
},
properties: {
chan: {
title: '产地',
type: 'string',
readOnly: true,
}
}
},
Text_3: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "外观尺寸"
},
properties: {
mian: {
title: '面料',
type: 'string',
readOnly: true,
},
yan: {
title: '颜色',
type: 'string',
readOnly: true,
},
ci: {
title: '尺码',
type: 'string',
readOnly: true,
}
}
},
Text_4: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "工艺"
},
properties: {
hua: {
title: '化学处理',
type: 'string',
readOnly: true,
},
biao: {
title: '表批量化',
type: 'string',
readOnly: true,
}
}
},
Text_5: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "特殊说明"
},
properties: {
specificInfo: {
title: '特殊说明',
type: 'string',
readOnly: true,
}
}
},
Text_6: {
type: "object",
"x-component": "CustomTitle",
"x-component-props": {
text: "附件"
},
properties: {
fileList: {
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',
},
}
},
}
}
// 选择商品抽屉高级筛选
export const productSearch: ISchema = {
type: 'object',
properties: {
groupName: {
type: 'string',
'x-component': 'ModalSearch',
'x-component-props': {
placeholder: '请输入会员名字',
placeholder: '属性组名称',
align: 'flex-left',
advanced: false,
},
},
[FORM_FILTER_PATH]: {
......@@ -19,9 +165,11 @@ export const formSearch: ISchema = {
'x-component': 'flex-layout',
'x-component-props': {
rowStyle: {
flexWrap: 'nowrap',
flexWrap: 'wrap',
width: '100%',
justifyContent: 'flex-start',
style: {
marginRight: 0
marginRight: 0,
}
},
colStyle: {
......@@ -29,13 +177,19 @@ export const formSearch: ISchema = {
},
},
properties: {
name: {
type: 'string',
'x-component-props': {
placeholder: '属性名称'
},
},
submit: {
"x-component": 'Submit',
"x-mega-props": {
span: 1
},
"x-component-props": {
children: '查询',
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