Merge remote-tracking branch 'origin/main'

perf/export-docx
Lei OT 11 months ago
commit 0f6cf8e1bc

@ -43,7 +43,7 @@ async function fetchLastRequet() {
const initialState = { const initialState = {
tokenInterval: null, tokenInterval: null,
tokenTimeout: false,// 开发时候用false正式环境true, tokenTimeout: import.meta.env.PROD ? true : false,
loginStatus: 0, loginStatus: 0,
defaltRoute: '', defaltRoute: '',
currentUser: { currentUser: {

@ -3,7 +3,7 @@ import { devtools } from 'zustand/middleware';
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { fetchJSON, postForm, postJSON } from '@/utils/request'; import { fetchJSON, postForm, postJSON } from '@/utils/request';
import { HT_HOST } from '@/config'; import { HT_HOST } from '@/config';
import { groupBy } from '@/utils/commons'; import { groupBy, generateId } from '@/utils/commons';
export const searchAgencyAction = async (param) => { export const searchAgencyAction = async (param) => {
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param);
@ -140,11 +140,17 @@ export const useProductsStore = create(
setActiveAgency: (activeAgency) => set({ activeAgency }), setActiveAgency: (activeAgency) => set({ activeAgency }),
setActiveAgencyState: (activeAgencyState) => set({ activeAgencyState }), setActiveAgencyState: (activeAgencyState) => set({ activeAgencyState }),
setAgencyProducts: (agencyProducts) => set({ agencyProducts }), setAgencyProducts: (agencyProducts) => set({ agencyProducts }),
// TODO产品和价格会分开查询编辑
setEditingProduct: (product) => { setEditingProduct: (product) => {
set(() => ({ set(() => ({
editingProduct: product, editingProduct: product,
quotationList: product?.quotation quotationList: (product?.quotation??[]).map(q => {
return {
...q,
key: generateId(),
fresh: false
}
})
})) }))
}, },
setEditing: (editing) => set({ editing }), setEditing: (editing) => set({ editing }),
@ -178,41 +184,101 @@ export const useProductsStore = create(
dayjs().startOf('M'), dayjs().startOf('M'),
dayjs().endOf('M') dayjs().endOf('M')
], ],
weekdays: '5, 6' weekdayList: ['5', '6'],
fresh: true // 标识是否是新记录,新记录才用添加列表
}), }),
// TODO添加价格后重新读取列表 appendQuotationList: (defList) => {
appendQuotationList: (newList) => { const { activeAgency, editingProduct, quotationList } = get()
set((state) => ({ const generatedList = []
quotationList: [...state.quotationList, ...newList]
defList.forEach(definition => {
const mappedPriceList = definition?.priceList.map(price => {
return {
id: null,
adult_cost: price.priceInput.audultPrice,
child_cost: price.priceInput.childrenPrice,
group_size_min: price.priceInput.numberStart,
group_size_max: price.priceInput.numberEnd,
currency: definition.currency,
unit_id: definition.unitId,
// 保持和 API 返回格式一致,日期要转换为字符串
use_dates_start: definition.useDate[0].format('YYYY-MM-DD'),
use_dates_end: definition.useDate[1].format('YYYY-MM-DD'),
weekdays: definition.weekend.join(','),
WPI_SN: editingProduct.info.id,
WPP_VEI_SN: activeAgency.travel_agency_id,
lastedit_changed: '',
audit_state_id: -1,
key: generateId(),
fresh: false
}
})
generatedList.push(...mappedPriceList)
})
const mergedList = [...quotationList,...generatedList]
set(() => ({
quotationList: mergedList
})) }))
return mergedList
}, },
saveOrUpdateQuotation: (formValues) => { saveOrUpdateQuotation: (formValues) => {
const { activeAgency, editingProduct, quotationList } = get() const { activeAgency, editingProduct, quotationList } = get()
let mergedList = []
formValues.WPI_SN = editingProduct.info.id formValues.WPI_SN = editingProduct.info.id
formValues.WPP_VEI_SN = activeAgency.travel_agency_id formValues.WPP_VEI_SN = activeAgency.travel_agency_id
formValues.use_dates_start = formValues.use_dates[0].format('YYYY-MM-DD') formValues.use_dates_start = formValues.use_dates[0].format('YYYY-MM-DD')
formValues.use_dates_end = formValues.use_dates[1].format('YYYY-MM-DD') formValues.use_dates_end = formValues.use_dates[1].format('YYYY-MM-DD')
formValues.weekdays = formValues.weekdayList.join(',')
if (formValues.fresh) {
formValues.key = generateId()
formValues.lastedit_changed = ''
formValues.audit_state_id = -1 // 新增,
formValues.fresh = false // 添加到列表后就不是新纪录,保存要修改原来记录
mergedList = [...quotationList,...[formValues]]
} else {
mergedList = quotationList.map(prevQuotation => {
if (prevQuotation.key === formValues.key) {
const changedList = []
for (const [key, value] of Object.entries(formValues)) {
if (key === 'use_dates' || key === 'id' || key === 'key') continue
const prevList = quotationList.filter(q => q.id === formValues.id) const preValue = prevQuotation[key]
const hasChanged = preValue !== value
if (prevList.length > 0) { if (hasChanged) {
const prevQuotation = prevList[0] changedList.push({
console.info('formValues: ', formValues) [key]: preValue,
console.info('prevQuotation: ', prevQuotation) })
// 对比报价前后是否有改动 }
for (const [key, value] of Object.entries(formValues)) { }
if (key === 'use_dates') continue
const prevValue = prevQuotation[key] return {
const hasChanged = prevValue === value ...prevQuotation,
console.log(`${key}: ${prevValue} - ${value} (${hasChanged})`) adult_cost: formValues.adult_cost,
} child_cost: formValues.child_cost,
currency: formValues.currency,
unit_id: formValues.unit_id,
group_size_min: formValues.group_size_min,
group_size_max: formValues.group_size_max,
use_dates_start: formValues.use_dates_start,
use_dates_end: formValues.use_dates_end,
weekdays: formValues.weekdays,
lastedit_changed: JSON.stringify(changedList, null, 2)
}
} else {
return prevQuotation
}
})
} }
const mergedList = [...quotationList,...[formValues]]
set(() => ({ set(() => ({
quotationList: mergedList quotationList: mergedList
})) }))
@ -221,14 +287,23 @@ export const useProductsStore = create(
}, },
deleteQuotation: (quotaionId) => { deleteQuotation: (quotaionId) => {
const { quotationList } = get() const { editingProduct, quotationList, agencyProducts } = get()
const productTypeId = editingProduct.info.product_type_id;
const newList = quotationList.filter(q => { const newList = quotationList.filter(q => {
return q.id != quotaionId return q.id != quotaionId
}) })
deleteQuotationAction(quotaionId) deleteQuotationAction(quotaionId)
set(() => ({
set({
agencyProducts: {
...agencyProducts,
[productTypeId]: [{
...editingProduct,
quotation: newList
}]
},
quotationList: newList quotationList: newList
})) })
}, },
// side effects // side effects

@ -82,25 +82,10 @@ export function isNotEmpty(val) {
return val !== undefined && val !== null && val !== ""; return val !== undefined && val !== null && val !== "";
} }
// export function isEmpty(val) {
// return val === undefined || val === null || val === "";
// }
export function prepareUrl(url) { export function prepareUrl(url) {
return new UrlBuilder(url); return new UrlBuilder(url);
} }
// export function debounce(fn, delay = 500) {
// let timer;
// return e => {
// e.persist();
// clearTimeout(timer);
// timer = setTimeout(() => {
// fn(e);
// }, delay);
// };
// }
export function throttle(fn, delay, atleast) { export function throttle(fn, delay, atleast) {
let timeout = null, let timeout = null,
startTime = new Date(); startTime = new Date();

@ -56,9 +56,6 @@ const ProductInfo = ({ ...props }) => {
const prevLgcDetailsMapped = editingProduct.lgc_details.reduce((r, c) => ({ ...r, [c.lgc]: { ...c, description: c.descriptions } }), {}); // todo: description const prevLgcDetailsMapped = editingProduct.lgc_details.reduce((r, c) => ({ ...r, [c.lgc]: { ...c, description: c.descriptions } }), {}); // todo: description
const mergedLgc = { ...prevLgcDetailsMapped, ...values.lgc_details_mapped }; const mergedLgc = { ...prevLgcDetailsMapped, ...values.lgc_details_mapped };
/** quotation */
const prevQuotationMapped = editingProduct.quotation.reduce((r, c) => ({ ...r, [c.id]: { ...c, unit: c.unit_id, audit_state: c.audit_state_id } }), {});
const mergedQ = { ...prevQuotationMapped, ...(values.quotation || []) };
// console.log(values); // console.log(values);
// return false; // debug: 0 // return false; // debug: 0
/** 提交保存 */ /** 提交保存 */
@ -67,7 +64,7 @@ const ProductInfo = ({ ...props }) => {
travel_agency_id: activeAgency.travel_agency_id, travel_agency_id: activeAgency.travel_agency_id,
info: readyToSubInfo, info: readyToSubInfo,
lgc_details: Object.values(mergedLgc), lgc_details: Object.values(mergedLgc),
quotation: Object.values(mergedQ), quotation: values.quotation,
}).catch(ex => { }).catch(ex => {
setLoading(false); setLoading(false);
notification.error({ notification.error({

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react' import { useState } from 'react'
import { Table, Form, Modal, Button, Radio, Input, Flex, Card, Select, Typography, InputNumber, Checkbox, DatePicker, Space } from 'antd' import { Table, Form, Modal, Button, Radio, Input, Flex, Card, Select, InputNumber, Checkbox, DatePicker, Space, App } from 'antd'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { CloseOutlined, StarTwoTone, PlusOutlined, ExclamationCircleFilled } from '@ant-design/icons'; import { CloseOutlined, StarTwoTone, PlusOutlined, ExclamationCircleFilled } from '@ant-design/icons'
import { useDatePresets } from '@/hooks/useDatePresets' import { useDatePresets } from '@/hooks/useDatePresets'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import useProductsStore from '@/stores/Products/Index' import useProductsStore from '@/stores/Products/Index'
@ -30,7 +30,7 @@ const PriceInput = (props) => {
return return
} }
if (!('numberStart' in value)) { if (!('numberStart' in value)) {
setNumberStart(newNumber); setNumberStart(newNumber)
} }
triggerChange({ triggerChange({
numberStart: newNumber, numberStart: newNumber,
@ -46,8 +46,8 @@ const PriceInput = (props) => {
} }
triggerChange({ triggerChange({
numberEnd: newNumber, numberEnd: newNumber,
}); })
}; }
const onAudultPriceChange = (e) => { const onAudultPriceChange = (e) => {
const newNumber = parseInt(e.target.value || '0', 10) const newNumber = parseInt(e.target.value || '0', 10)
if (Number.isNaN(audultPrice)) { if (Number.isNaN(audultPrice)) {
@ -71,7 +71,7 @@ const PriceInput = (props) => {
triggerChange({ triggerChange({
childrenPrice: newNumber, childrenPrice: newNumber,
}) })
}; }
return ( return (
<Space.Compact id={id}> <Space.Compact id={id}>
<Input <Input
@ -121,7 +121,7 @@ const batchSetupInitialValues = {
dayjs().add(1, 'year').startOf('y'), dayjs().add(1, 'year').endOf('y') dayjs().add(1, 'year').startOf('y'), dayjs().add(1, 'year').endOf('y')
], ],
'unitId': '0', 'unitId': '0',
'currency': 'CNY', 'currency': 'RMB',
'weekend': [ 'weekend': [
'5', '5',
'6' '6'
@ -167,7 +167,7 @@ const batchSetupInitialValues = {
dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M') dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M')
], ],
'unitId': '0', 'unitId': '0',
'currency': 'CNY', 'currency': 'RMB',
'weekend': [ 'weekend': [
'5', '5',
'6' '6'
@ -224,7 +224,7 @@ const defaultDefinitionValue = {
dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M') dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M')
], ],
'unitId': '0', 'unitId': '0',
'currency': 'CNY', 'currency': 'RMB',
'weekend': [ 'weekend': [
'5', '5',
'6' '6'
@ -242,6 +242,7 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
const [isQuotationModalOpen, setQuotationModalOpen] = useState(false) const [isQuotationModalOpen, setQuotationModalOpen] = useState(false)
const [isBatchSetupModalOpen, setBatchSetupModalOpen] = useState(false) const [isBatchSetupModalOpen, setBatchSetupModalOpen] = useState(false)
const { modal } = App.useApp();
const [quotationForm] = Form.useForm() const [quotationForm] = Form.useForm()
const [batchSetupForm] = Form.useForm() const [batchSetupForm] = Form.useForm()
@ -259,6 +260,7 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
const onQuotationSeleted = async (quotation) => { const onQuotationSeleted = async (quotation) => {
// start, end RangePicker // start, end RangePicker
quotation.use_dates = [dayjs(quotation.use_dates_start), dayjs(quotation.use_dates_end)] quotation.use_dates = [dayjs(quotation.use_dates_start), dayjs(quotation.use_dates_end)]
quotation.weekdayList = quotation.weekdays.split(',')
quotationForm.setFieldsValue(quotation) quotationForm.setFieldsValue(quotation)
setQuotationModalOpen(true) setQuotationModalOpen(true)
} }
@ -276,30 +278,9 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
} }
const onBatchSetupFinish = () => { const onBatchSetupFinish = () => {
let priceList = []
const defList = batchSetupForm.getFieldsValue().defList const defList = batchSetupForm.getFieldsValue().defList
const newList = appendQuotationList(defList)
defList.forEach(definition => { triggerChange(newList)
const mappedPriceList = definition?.priceList.map(price => {
return {
id: null,
adult_cost: price.priceInput.audultPrice,
child_cost: price.priceInput.childrenPrice,
group_size_min: price.priceInput.numberStart,
group_size_max: price.priceInput.numberEnd,
currency: definition.currency,
unit_id: definition.unitId,
// API
use_dates_start: definition.useDate[0].format('YYYY-MM-DD'),
use_dates_end: definition.useDate[1].format('YYYY-MM-DD'),
weekdays: definition.weekend.join(','),
}
})
priceList.push(...mappedPriceList)
})
appendQuotationList(priceList)
setBatchSetupModalOpen(false) setBatchSetupModalOpen(false)
} }
@ -337,16 +318,13 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
<Space> <Space>
<Button type='link' onClick={() => onQuotationSeleted(quotation)}>{t('Edit')}</Button> <Button type='link' onClick={() => onQuotationSeleted(quotation)}>{t('Edit')}</Button>
<Button type='link' danger onClick={() => { <Button type='link' danger onClick={() => {
Modal.confirm({ modal.confirm({
title: '请确认', title: '请确认',
icon: <ExclamationCircleFilled />, icon: <ExclamationCircleFilled />,
content: '你要删除这条价格吗?', content: '你要删除这条价格吗?',
onOk() { onOk() {
deleteQuotation(quotation.id) deleteQuotation(quotation.id)
}, },
onCancel() {
console.log('Cancel');
},
}) })
}}>{t('Delete')}</Button> }}>{t('Delete')}</Button>
</Space> </Space>
@ -359,7 +337,6 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
<> <>
<h2>{t('products:EditComponents.Quotation')}</h2> <h2>{t('products:EditComponents.Quotation')}</h2>
<Table <Table
// rowKey={'id'}
bordered bordered
dataSource={quotationList} dataSource={quotationList}
columns={quotationColumns} columns={quotationColumns}
@ -408,7 +385,7 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
> >
<Form.Item label='币种' name={[field.name, 'currency']}> <Form.Item label='币种' name={[field.name, 'currency']}>
<Select placeholder='选择币种'> <Select placeholder='选择币种'>
<Select.Option value='CNY'>CNY</Select.Option> <Select.Option value='RMB'>RMB</Select.Option>
<Select.Option value='USD'>USD</Select.Option> <Select.Option value='USD'>USD</Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
@ -453,13 +430,6 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
</Flex> </Flex>
)} )}
</Form.List> </Form.List>
<Form.Item noStyle shouldUpdate>
{() => (
<Typography className='hidden'>
<pre>{JSON.stringify(batchSetupForm.getFieldsValue(), null, 2)}</pre>
</Typography>
)}
</Form.Item>
</Form> </Form>
</Modal> </Modal>
@ -488,6 +458,8 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
)} )}
> >
<Form.Item name='id' className='hidden' ><Input /></Form.Item> <Form.Item name='id' className='hidden' ><Input /></Form.Item>
<Form.Item name='key' className='hidden' ><Input /></Form.Item>
<Form.Item name='fresh' className='hidden' ><Input /></Form.Item>
<Form.Item <Form.Item
label={t('products:adultPrice')} label={t('products:adultPrice')}
name='adult_cost' name='adult_cost'
@ -580,7 +552,7 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
</Form.Item> </Form.Item>
<Form.Item <Form.Item
label={t('products:Weekdays')} label={t('products:Weekdays')}
name='weekdays' name='weekdayList'
> >
<Checkbox.Group options={['5', '6', '7']} /> <Checkbox.Group options={['5', '6', '7']} />
</Form.Item> </Form.Item>

@ -148,8 +148,6 @@ function Newest() {
// //
const searchReservation = (submitValues, current=1) => { const searchReservation = (submitValues, current=1) => {
setDataLoading(true) setDataLoading(true)
console.info('onSearchClick')
console.info(submitValues)
fetchReservationList(submitValues, current) fetchReservationList(submitValues, current)
.catch(ex => { .catch(ex => {
notification.error({ notification.error({

Loading…
Cancel
Save