开发报价新增表单

perf/export-docx
Jimmy Liow 11 months ago
parent a63887a7b6
commit b6e57cd4ba

@ -288,14 +288,7 @@ const BatchImportPrice = ({ onBatchImportData }) => {
"6" "6"
], ],
"priceList": [ "priceList": [
{ // defaultPriceValue
"priceInput": {
"numberStart": 1,
"numberEnd": 2,
"audultPrice": 0,
"childrenPrice": 0
}
},
] ]
} }

@ -89,7 +89,6 @@ const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, s
{/* <Divider className='my-1' /> */} {/* <Divider className='my-1' /> */}
<ProductInfoLgc editable={editable} formInstance={form} /> <ProductInfoLgc editable={editable} formInstance={form} />
<ProductInfoQuotation editable={editable} formInstance={form} />
<Form.Item hidden name={'id'} label={'ID'}> <Form.Item hidden name={'id'} label={'ID'}>
<Input /> <Input />
@ -104,6 +103,7 @@ const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, s
</Form.Item> </Form.Item>
)} )}
</Form> </Form>
<ProductInfoQuotation editable={editable} />
</> </>
); );
}; };

@ -1,130 +1,130 @@
import { createContext, useEffect, useState } from 'react'; import { useState } from 'react'
import { Table, Form, Modal, Button, InputNumber, Select, DatePicker, Popconfirm } from 'antd'; import { Table, Form, Modal, Button, Radio, Input, InputNumber, Checkbox, DatePicker, Space } from 'antd'
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next'
import dayjs from 'dayjs'; import dayjs from 'dayjs'
import BatchImportPrice from './BatchImportPrice'; import BatchImportPrice from './BatchImportPrice'
import { useDatePresets, useWeekdays } from '@/hooks/useDatePresets'; import { useDatePresets } from '@/hooks/useDatePresets'
import useProductsStore from '@/stores/Products/Index'
const { RangePicker } = DatePicker
const ProductInfoQuotation = ({ editable, ...props }) => {
const { t } = useTranslation()
const [isQuotationModalOpen, setQuotationModalOpen] = useState(false)
const [quotationForm] = Form.useForm()
const datePresets = useDatePresets()
const [editingProduct] = useProductsStore((state) => [state.editingProduct])
const [batchImportPriceVisible, setBatchImportPriceVisible] = useState(false)
const [quotationTableVisible, setQuotationTableVisible] = useState(false)
const [quotation, setQuotation] = useState([])
const [batchImportData, setBatchImportData] = useState([])
const { RangePicker } = DatePicker;
const ProductInfoQuotation = ({ editable, formInstance, ...props }) => {
const { t } = useTranslation();
const presets = useDatePresets();
const weekdaySets = useWeekdays();
const [batchImportPriceVisible, setBatchImportPriceVisible] = useState(false);
const [quotationTableVisible, setQuotationTableVisible] = useState(false);
const [editIndex, setEditIndex] = useState(null);
const [quotation, setQuotation] = useState([]);
const [batchImportData, setBatchImportData] = useState([]);
const [weekdays, setWeekdays] = useState([]);
const [selectedDays, setSelectedDays] = useState([]);
const [currentQuotationRecord, setCurrentQuotationRecord] = useState({
use_dates_start: null,
use_dates_end: null,
});
const edit = (record, index) => {
setQuotationTableVisible(true);
setEditIndex(index);
setCurrentQuotationRecord(record);
};
const handleAdd = () => {
const newData = {
adult_cost: 0,
child_cost: 0,
currency: '',
group_size_min: 0,
group_size_max: 0,
id: '',
lastedit_changed: '',
use_dates_start: '',
use_dates_end: '',
weekdays: '',
tempKey: Math.random(),
};
setQuotation([...quotation, newData]);
const index = [...quotation, newData].length - 1;
edit(newData, index);
};
const handleBatchImportData = (data) => { const handleBatchImportData = (data) => {
setBatchImportData(data); setBatchImportData(data)
}; }
const handleBatchImport = () => {
setBatchImportPriceVisible(true);
};
const handleBatchImportOK = () => { const handleBatchImportOK = () => {
const tempBatchImportData = batchImportData.map((item) => { const tempBatchImportData = batchImportData.map((item) => {
const { tag, validPeriod, ...rest } = item; const { tag, validPeriod, ...rest } = item
return rest; return rest
}); })
const newData = [...quotation, ...tempBatchImportData]; const newData = [...quotation, ...tempBatchImportData]
const sortedData = [...newData].sort((a, b) => { const sortedData = [...newData].sort((a, b) => {
if (a.group_size_min !== b.group_size_min) { if (a.group_size_min !== b.group_size_min) {
return a.group_size_min - b.group_size_min; return a.group_size_min - b.group_size_min
} }
return a.group_size_max - b.group_size_max; return a.group_size_max - b.group_size_max
}); })
setQuotation(sortedData); setQuotation(sortedData)
setBatchImportPriceVisible(false); setBatchImportPriceVisible(false)
}; }
const quotationTableVisibleOK = () => { const quotationTableVisibleOK = () => {
currentQuotationRecord.use_dates_start = dayjs(currentQuotationRecord.use_dates_start).format('YYYY-MM-DD'); const tempQuotation = [...quotation]
currentQuotationRecord.use_dates_end = dayjs(currentQuotationRecord.use_dates_end).format('YYYY-MM-DD');
const tempQuotation = [...quotation];
tempQuotation[editIndex] = { ...currentQuotationRecord, weekdays: weekdays };
const sortedData = [...tempQuotation].sort((a, b) => { const sortedData = [...tempQuotation].sort((a, b) => {
const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start)); const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start))
const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start)); const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start))
if (aValidPeriod !== bValidPeriod) { if (aValidPeriod !== bValidPeriod) {
return aValidPeriod - bValidPeriod; return aValidPeriod - bValidPeriod
} }
const aGroupSize = a.group_size_max - a.group_size_min; const aGroupSize = a.group_size_max - a.group_size_min
const bGroupSize = b.group_size_max - b.group_size_min; const bGroupSize = b.group_size_max - b.group_size_min
return aGroupSize - bGroupSize; return aGroupSize - bGroupSize
}); })
setQuotation(sortedData); setQuotation(sortedData)
setQuotationTableVisible(false); setQuotationTableVisible(false)
}; }
const quotationTableVisibleCancel = () => { const quotationTableVisibleCancel = () => {
setQuotationTableVisible(false); setQuotationTableVisible(false)
}; }
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
const handleDayClick = (dayOfWeek) => {
setSelectedDays((prevSelectedDays) => {
const updatedDays = prevSelectedDays.includes(dayOfWeek) ? prevSelectedDays.filter((d) => d !== dayOfWeek) : [...prevSelectedDays, dayOfWeek];
const weekdaysString = updatedDays.sort().join(',');
setWeekdays(weekdaysString);
return updatedDays;
});
};
const handleDelete = (index) => { const handleDelete = (index) => {
const newData = [...quotation]; const newData = [...quotation]
newData.splice(index, 1); newData.splice(index, 1)
const sortedData = [...newData].sort((a, b) => { const sortedData = [...newData].sort((a, b) => {
const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start)); const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start))
const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start)); const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start))
if (aValidPeriod !== bValidPeriod) { if (aValidPeriod !== bValidPeriod) {
return aValidPeriod - bValidPeriod; return aValidPeriod - bValidPeriod
}
const aGroupSize = a.group_size_max - a.group_size_min
const bGroupSize = b.group_size_max - b.group_size_min
return aGroupSize - bGroupSize
})
setQuotation(sortedData)
} }
const aGroupSize = a.group_size_max - a.group_size_min;
const bGroupSize = b.group_size_max - b.group_size_min;
return aGroupSize - bGroupSize; const onQuotationSeleted = async (quotation) => {
}); // RangePicker
setQuotation(sortedData); quotation.use_dates = [dayjs(quotation.use_dates_start), dayjs(quotation.use_dates_end)]
}; quotationForm.setFieldsValue(quotation)
const columns = [ setQuotationModalOpen(true)
}
const onNewQuotation = () => {
// const emptyQuotation = newEmptyQuotation()
// quotationForm.setFieldsValue(emptyQuotation)
// setQuotationModalOpen(true)
}
const onQuotationFinish = (values) => {
console.info(values)
// saveOrUpdateQuotation(values)
// .then(() => {
// setQuotationModalOpen(false)
// })
// .catch(ex => {
// notification.error({
// message: 'Notification',
// description: ex.message,
// placement: 'top',
// duration: 4,
// })
// })
}
const onQuotationFailed = (error) => {
console.log('Failed:', error)
// form.resetFields()
}
const quotationColumns = [
{ title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '4rem' }, { title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '4rem' },
{ title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '4rem' }, { title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '4rem' },
{ title: t('products:currency'), dataIndex: 'currency', width: '4rem' }, { title: t('products:currency'), dataIndex: 'currency', width: '4rem' },
{ {
title: t('products:Types'), title: t('products:Types'),
dataIndex: 'unit', dataIndex: 'unit_name',
width: '4rem', width: '4rem',
render: (text) => (text === '0' ? '每人' : text === '1' ? '每团' : text), render: (text) => (text === '0' ? '每人' : text === '1' ? '每团' : text),
}, },
@ -137,129 +137,180 @@ const ProductInfoQuotation = ({ editable, formInstance, ...props }) => {
{ {
title: t('products:validityPeriod'), title: t('products:validityPeriod'),
dataIndex: 'validityPeriod', dataIndex: 'use_dates',
width: '6rem', width: '6rem',
render: (_, record) => `${record.use_dates_start}-${record.use_dates_end}`, render: (_, record) => `${record.use_dates_start}-${record.use_dates_end}`,
}, },
{ title: t('products:Weekdays'), dataIndex: 'weekdays', width: '4rem' }, { title: t('products:Weekdays'), dataIndex: 'weekdays', width: '4rem' },
...(editable
? [
{ {
title: t('products:operation'), title: t('products:operation'),
dataIndex: 'operation', dataIndex: 'operation',
width: '6rem', width: '3%',
render: (_, record, index) => { render: (_, quotation) => {
const canEdit = true; // record.audit_state_id === -1; // todo:
return ( return (
<span> <Space>
{canEdit ? ( <Button type='link' onClick={() => onQuotationSeleted(quotation)}>{t('Edit')}</Button>
<a onClick={() => edit(record, index)} style={{ marginRight: 8 }}> <Button type='link' danger onClick={() => handleDelete(quotation)}>{t('Delete')}</Button>
{t('Edit')} </Space>
</a> )
) : (
<span style={{ color: 'gray', marginRight: 8 }}>{t('Edit')}</span>
)}
{canEdit ? (
<Popconfirm title={t('sureDelete')} onConfirm={() => handleDelete(index)}>
<a>{t('Delete')}</a>
</Popconfirm>
) : (
<span style={{ color: 'gray' }}>{t('Delete')}</span>
)}
</span>
);
}, },
}, },
] ]
: []),
];
return ( return (
<> <>
<h2>{t('products:EditComponents.Quotation')}</h2> <h2>{t('products:EditComponents.Quotation')}</h2>
<Form.Item name='quotation'>
<Table <Table
rowKey={'tempKey'} rowKey={'id'}
bordered bordered
dataSource={quotation} dataSource={editingProduct.quotation}
columns={columns} columns={quotationColumns}
rowClassName='editable-row' pagination={false}
// pagination={{ onChange: cancel }}
/> />
{ {
editable && // editable &&
<Button onClick={handleAdd} type='primary' ghost style={{ marginTop: 16 }}> <Button onClick={() => setQuotationModalOpen(true)} type='primary' ghost style={{ marginTop: 16 }}>
{t('products:addQuotation')} {t('products:addQuotation')}
</Button> </Button>
} }
{ {
editable && // editable &&
<Button onClick={handleBatchImport} type='primary' ghost style={{ marginTop: 16, marginLeft: 16 }}> <Button onClick={() => setBatchImportPriceVisible(true)} type='primary' ghost style={{ marginTop: 16, marginLeft: 16 }}>
批量添加 批量添加
</Button> </Button>
} }
</Form.Item>
<Modal title='批量设置价格' open={batchImportPriceVisible} onOk={handleBatchImportOK} onCancel={() => setBatchImportPriceVisible(false)} width={'90%'}> <Modal title='批量设置价格' open={batchImportPriceVisible} onOk={handleBatchImportOK} onCancel={() => setBatchImportPriceVisible(false)} width={'90%'}>
<BatchImportPrice onBatchImportData={handleBatchImportData} /> <BatchImportPrice onBatchImportData={handleBatchImportData} />
</Modal> </Modal>
<Modal title='编辑供应商报价' open={quotationTableVisible} onOk={quotationTableVisibleOK} onCancel={quotationTableVisibleCancel}> <Modal
<h3>成人价</h3> centered
<InputNumber value={currentQuotationRecord.adult_cost} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, adult_cost: e })} /> okButtonProps={{
<h3>儿童价</h3> autoFocus: true,
<InputNumber value={currentQuotationRecord.child_cost} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, child_cost: e })} /> htmlType: 'submit',
<h3>币种</h3>
<Select style={{ width: '30%' }} value={currentQuotationRecord.currency} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, currency: e })}>
<Select.Option value='rmb'>rmb</Select.Option>
<Select.Option value='usd'>usd</Select.Option>
</Select>
<h3>类型</h3>
<Select style={{ width: '30%' }} value={currentQuotationRecord.unit} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, unit: e })}>
<Select.Option value='0'>每人</Select.Option>
<Select.Option value='1'>每团</Select.Option>
</Select>
<h3>人等</h3>
<td style={{ display: 'flex', alignItems: 'center' }}>
<InputNumber
min={0}
value={currentQuotationRecord.group_size_min}
onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_min: e })}
style={{ width: '50%', marginRight: '10px' }}
/>
<span>-</span>
<InputNumber
min={0}
value={currentQuotationRecord.group_size_max}
onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_max: e })}
style={{ width: '50%', marginLeft: '10px' }}
/>
</td>
<h3>有效期</h3>
<RangePicker
allowClear={true}
inputReadOnly={true}
presets={presets}
placeholder={['From', 'Thru']}
// value={startDate && endDate ? [startDate, endDate] : null}
onChange={(dates) => {
setCurrentQuotationRecord({
...currentQuotationRecord,
use_dates_start: dates[0],
use_dates_end: dates[1],
});
}} }}
/> title={t('account:detail')}
<h3>周末</h3> open={isQuotationModalOpen} onCancel={() => setQuotationModalOpen(false)}
{weekdaySets.map((day, index) => ( destroyOnClose
<Button key={day.value} type={selectedDays.includes(day.value) ? 'primary' : 'default'} onClick={() => handleDayClick(day.value)} style={{ margin: '5px' }}> forceRender
{day.label} modalRender={(dom) => (
</Button> <Form
))} name='quotationForm'
form={quotationForm}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
className='max-w-2xl'
onFinish={onQuotationFinish}
onFinishFailed={onQuotationFailed}
autoComplete='off'
>
{dom}
</Form>
)}
>
<Form.Item name='id' className='hidden' ><Input /></Form.Item>
<Form.Item
label={t('products:adultPrice')}
name='adult_cost'
rules={[
{
required: true,
message: t('products:Validation.adultPrice'),
},
]}
>
<InputNumber style={{width: '100%'}} />
</Form.Item>
<Form.Item
label={t('products:childrenPrice')}
name='child_cost'
rules={[
{
required: true,
message: t('products:Validation.childrenPrice'),
},
]}
>
<InputNumber style={{width: '100%'}} />
</Form.Item>
<Form.Item
label={t('products:currency')}
name='currency'
rules={[
{
required: true,
message: t('products:Validation.currency'),
},
]}
>
<Radio.Group>
<Radio value='RMB'>RMB</Radio>
<Radio value='USD'>USD</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label={t('products:Types')}
name='unit_name'
rules={[
{
required: true,
message: t('products:Validation.unit_name'),
},
]}
>
<Radio.Group>
<Radio value='0'>每人</Radio>
<Radio value='1'>每团</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label={t('products:number')}
name='group_size_min'
rules={[
{
required: true,
message: t('products:Validation.group_size_min'),
},
]}
>
<InputNumber style={{width: '100%'}} />
</Form.Item>
<Form.Item
label={t('products:number')}
name='group_size_max'
rules={[
{
required: true,
message: t('products:Validation.group_size_max'),
},
]}
>
<InputNumber style={{width: '100%'}} />
</Form.Item>
<Form.Item
label={t('products:validityPeriod')}
name='use_dates'
rules={[
{
required: true,
message: t('products:Validation.use_dates'),
},
]}
>
<RangePicker presets={datePresets} style={{width: '100%'}} />
</Form.Item>
<Form.Item
label={t('products:Weekdays')}
name='weekdays'
>
<Checkbox.Group options={['5', '6', '7']} />
</Form.Item>
</Modal> </Modal>
</> </>
); )
}; }
export default ProductInfoQuotation;
export default ProductInfoQuotation

Loading…
Cancel
Save