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 = {
tokenInterval: null,
tokenTimeout: false,// 开发时候用false正式环境true,
tokenTimeout: import.meta.env.PROD ? true : false,
loginStatus: 0,
defaltRoute: '',
currentUser: {

@ -3,7 +3,7 @@ import { devtools } from 'zustand/middleware';
import dayjs from 'dayjs'
import { fetchJSON, postForm, postJSON } from '@/utils/request';
import { HT_HOST } from '@/config';
import { groupBy } from '@/utils/commons';
import { groupBy, generateId } from '@/utils/commons';
export const searchAgencyAction = async (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 }),
setActiveAgencyState: (activeAgencyState) => set({ activeAgencyState }),
setAgencyProducts: (agencyProducts) => set({ agencyProducts }),
// TODO产品和价格会分开查询编辑
setEditingProduct: (product) => {
set(() => ({
editingProduct: product,
quotationList: product?.quotation
quotationList: (product?.quotation??[]).map(q => {
return {
...q,
key: generateId(),
fresh: false
}
})
}))
},
setEditing: (editing) => set({ editing }),
@ -178,41 +184,101 @@ export const useProductsStore = create(
dayjs().startOf('M'),
dayjs().endOf('M')
],
weekdays: '5, 6'
weekdayList: ['5', '6'],
fresh: true // 标识是否是新记录,新记录才用添加列表
}),
// TODO添加价格后重新读取列表
appendQuotationList: (newList) => {
set((state) => ({
quotationList: [...state.quotationList, ...newList]
appendQuotationList: (defList) => {
const { activeAgency, editingProduct, quotationList } = get()
const generatedList = []
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) => {
const { activeAgency, editingProduct, quotationList } = get()
let mergedList = []
formValues.WPI_SN = editingProduct.info.id
formValues.WPP_VEI_SN = activeAgency.travel_agency_id
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.weekdays = formValues.weekdayList.join(',')
const prevList = quotationList.filter(q => q.id === formValues.id)
if (prevList.length > 0) {
const prevQuotation = prevList[0]
console.info('formValues: ', formValues)
console.info('prevQuotation: ', prevQuotation)
// 对比报价前后是否有改动
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') continue
if (key === 'use_dates' || key === 'id' || key === 'key') continue
const prevValue = prevQuotation[key]
const hasChanged = prevValue === value
console.log(`${key}: ${prevValue} - ${value} (${hasChanged})`)
const preValue = prevQuotation[key]
const hasChanged = preValue !== value
if (hasChanged) {
changedList.push({
[key]: preValue,
})
}
}
const mergedList = [...quotationList,...[formValues]]
return {
...prevQuotation,
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
}
})
}
set(() => ({
quotationList: mergedList
}))
@ -221,14 +287,23 @@ export const useProductsStore = create(
},
deleteQuotation: (quotaionId) => {
const { quotationList } = get()
const { editingProduct, quotationList, agencyProducts } = get()
const productTypeId = editingProduct.info.product_type_id;
const newList = quotationList.filter(q => {
return q.id != quotaionId
})
deleteQuotationAction(quotaionId)
set(() => ({
set({
agencyProducts: {
...agencyProducts,
[productTypeId]: [{
...editingProduct,
quotation: newList
}]
},
quotationList: newList
}))
})
},
// side effects

@ -82,25 +82,10 @@ export function isNotEmpty(val) {
return val !== undefined && val !== null && val !== "";
}
// export function isEmpty(val) {
// return val === undefined || val === null || val === "";
// }
export function prepareUrl(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) {
let timeout = null,
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 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);
// return false; // debug: 0
/** 提交保存 */
@ -67,7 +64,7 @@ const ProductInfo = ({ ...props }) => {
travel_agency_id: activeAgency.travel_agency_id,
info: readyToSubInfo,
lgc_details: Object.values(mergedLgc),
quotation: Object.values(mergedQ),
quotation: values.quotation,
}).catch(ex => {
setLoading(false);
notification.error({

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

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

Loading…
Cancel
Save