diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 0c48ce6..7fc81b8 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -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: { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 94e655b..b22dd87 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -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(',') + + 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) { - const prevQuotation = prevList[0] - console.info('formValues: ', formValues) - console.info('prevQuotation: ', prevQuotation) - // 对比报价前后是否有改动 - for (const [key, value] of Object.entries(formValues)) { - if (key === 'use_dates') continue + if (hasChanged) { + changedList.push({ + [key]: preValue, + }) + } + } - const prevValue = prevQuotation[key] - const hasChanged = prevValue === value - console.log(`${key}: ${prevValue} - ${value} (${hasChanged})`) - } + 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 + } + }) } - const mergedList = [...quotationList,...[formValues]] 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 diff --git a/src/utils/commons.js b/src/utils/commons.js index 6c1f0b4..0e93cc5 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -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(); diff --git a/src/views/products/Detail/ProductInfo.jsx b/src/views/products/Detail/ProductInfo.jsx index 56f493a..99806fc 100644 --- a/src/views/products/Detail/ProductInfo.jsx +++ b/src/views/products/Detail/ProductInfo.jsx @@ -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({ diff --git a/src/views/products/Detail/ProductInfoQuotation.jsx b/src/views/products/Detail/ProductInfoQuotation.jsx index de6737c..c21b744 100644 --- a/src/views/products/Detail/ProductInfoQuotation.jsx +++ b/src/views/products/Detail/ProductInfoQuotation.jsx @@ -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 ( { 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 }) => { @@ -359,7 +337,6 @@ const ProductInfoQuotation = ({ editable, ...props }) => { <>

{t('products:EditComponents.Quotation')}

{ > @@ -453,13 +430,6 @@ const ProductInfoQuotation = ({ editable, ...props }) => { )} - - {() => ( - -
{JSON.stringify(batchSetupForm.getFieldsValue(), null, 2)}
-
- )} -
@@ -488,6 +458,8 @@ const ProductInfoQuotation = ({ editable, ...props }) => { )} > + + { diff --git a/src/views/reservation/Newest.jsx b/src/views/reservation/Newest.jsx index 349d40e..32b2630 100644 --- a/src/views/reservation/Newest.jsx +++ b/src/views/reservation/Newest.jsx @@ -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({