From b8b679a0ecbb610b9c3f6673fedda78667ca53dd Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Thu, 1 Aug 2024 13:34:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=B9=E9=87=8F=E8=AE=BE=E7=BD=AE=E4=BB=B7?= =?UTF-8?q?=E6=A0=BC=E5=90=8E=E9=A9=AC=E4=B8=8A=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Account.js | 12 +- src/stores/Products/Index.js | 31 +- .../products/Detail/BatchImportPrice.jsx | 18 +- .../products/Detail/ProductInfoQuotation.jsx | 460 ++++++++++++++---- 4 files changed, 413 insertions(+), 108 deletions(-) diff --git a/src/stores/Account.js b/src/stores/Account.js index f11f545..195c46b 100644 --- a/src/stores/Account.js +++ b/src/stores/Account.js @@ -91,13 +91,11 @@ const useAccountStore = create((set, get) => ({ return postAccountPassword(formData) }, - newEmptyRole: () => { - return { - role_id: null, - role_name: '', - role_ids: '' - } - }, + newEmptyRole: () => ({ + role_id: null, + role_name: '', + role_ids: '' + }), newEmptyAccount: () => { return { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 1b7c10b..ae7d0d7 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -1,6 +1,6 @@ import { create } from 'zustand'; 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'; @@ -107,6 +107,7 @@ const initialState = { activeAgency: {}, // 审核/编辑 页: 当前的供应商 agencyProducts: {}, // 审核/编辑 页: 供应商产品列表 editingProduct: {}, // 编辑页: 当前编辑的产品 + quotationList: [], // 编辑页: 当前产品报价列表 editing: false, }; export const useProductsStore = create( @@ -120,11 +121,37 @@ export const useProductsStore = create( setAgencyList: (agencyList) => set({ agencyList }), setActiveAgency: (activeAgency) => set({ activeAgency }), setAgencyProducts: (agencyProducts) => set({ agencyProducts }), - setEditingProduct: (editingProduct) => set({ editingProduct }), + setEditingProduct: (product) => { + set(() => ({ + editingProduct: product, + quotationList: product.quotation + })) + }, setEditing: (editing) => set({ editing }), reset: () => set(initialState), + newEmptyQuotation: () => ({ + id: null, + adult_cost: 0, + child_cost: 0, + currency: 'RMB', + unit_id: '0', + group_size_min: 1, + group_size_max: 10, + use_dates: [ + dayjs().startOf('M'), + dayjs().endOf('M') + ], + weekdays: '5, 6' + }), + + appendQuotationList: (newList) => { + set((state) => ({ + quotationList: [...state.quotationList, ...newList] + })) + }, + // side effects searchAgency: async (param) => { const { setLoading, setAgencyList } = get(); diff --git a/src/views/products/Detail/BatchImportPrice.jsx b/src/views/products/Detail/BatchImportPrice.jsx index 535d811..935072b 100644 --- a/src/views/products/Detail/BatchImportPrice.jsx +++ b/src/views/products/Detail/BatchImportPrice.jsx @@ -56,6 +56,7 @@ const BatchImportPrice = ({ onBatchImportData }) => { currency: definition.currency, unit: definition.unitName, + // 保持和 API 返回格式一致,日期要转换为字符串 use_dates_start: definition.useDate[0], use_dates_end: definition.useDate[1], weekdays: definition.weekend.join(','), @@ -315,27 +316,26 @@ const BatchImportPrice = ({ onBatchImportData }) => { remove(field.name) }} />} > - - - - - - + + + + + {(priceFieldList, priceOptList) => ( diff --git a/src/views/products/Detail/ProductInfoQuotation.jsx b/src/views/products/Detail/ProductInfoQuotation.jsx index 235310d..fa8438c 100644 --- a/src/views/products/Detail/ProductInfoQuotation.jsx +++ b/src/views/products/Detail/ProductInfoQuotation.jsx @@ -1,104 +1,273 @@ -import { useState } from 'react' -import { Table, Form, Modal, Button, Radio, Input, InputNumber, Checkbox, DatePicker, Space } from 'antd' +import { useEffect, useState } from 'react' +import { Table, Form, Modal, Button, Radio, Input, Flex, Card, Select, Typography, InputNumber, Checkbox, DatePicker, Space } from 'antd' import { useTranslation } from 'react-i18next' import dayjs from 'dayjs' -import BatchImportPrice from './BatchImportPrice' +import { CloseOutlined, StarTwoTone, PlusOutlined } from '@ant-design/icons'; 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 handleBatchImportData = (data) => { - setBatchImportData(data) +const PriceInput = (props) => { + const { id, value = {}, onChange } = props + const [numberStart, setNumberStart] = useState(0) + const [numberEnd, setNumberEnd] = useState(0) + const [audultPrice, setAudultPrice] = useState(0) + const [childrenPrice, setChildrenPrice] = useState(0) + const triggerChange = (changedValue) => { + onChange?.({ + numberStart, + numberEnd, + audultPrice, + childrenPrice, + ...value, + ...changedValue, + }) } - - const handleBatchImportOK = () => { - const tempBatchImportData = batchImportData.map((item) => { - const { tag, validPeriod, ...rest } = item - return rest + const onNumberStartChange = (e) => { + const newNumber = parseInt(e.target.value || '0', 10) + if (Number.isNaN(numberStart)) { + return + } + if (!('numberStart' in value)) { + setNumberStart(newNumber); + } + triggerChange({ + numberStart: newNumber, }) - const newData = [...quotation, ...tempBatchImportData] - const sortedData = [...newData].sort((a, b) => { - if (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 + } + const onNumberEndChange = (e) => { + const newNumber = parseInt(e.target.value || '0', 10) + if (Number.isNaN(numberEnd)) { + return + } + if (!('numberEnd' in value)) { + setNumberEnd(newNumber) + } + triggerChange({ + numberEnd: newNumber, + }); + }; + const onAudultPriceChange = (e) => { + const newNumber = parseInt(e.target.value || '0', 10) + if (Number.isNaN(audultPrice)) { + return + } + if (!('audultPrice' in value)) { + setAudultPrice(newNumber) + } + triggerChange({ + audultPrice: newNumber, + }) + } + const onChildrenPriceChange = (e) => { + const newNumber = parseInt(e.target.value || '0', 10) + if (Number.isNaN(childrenPrice)) { + return + } + if (!('childrenPrice' in value)) { + setChildrenPrice(newNumber) + } + triggerChange({ + childrenPrice: newNumber, }) + }; + return ( + + + + + + + ) +} + +const batchSetupInitialValues = { + 'defList': [ + // 旺季 + { + 'useDate': [ + dayjs().add(1, 'year').startOf('y'), dayjs().add(1, 'year').endOf('y') + ], + 'unitName': '每人', + 'currency': 'CNY', + 'weekend': [ + '5', + '6' + ], + 'priceList': [ + { + 'priceInput': { + 'numberStart': 1, + 'numberEnd': 2, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 3, + 'numberEnd': 4, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 5, + 'numberEnd': 6, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 7, + 'numberEnd': 9, + 'audultPrice': 0, + 'childrenPrice': 0 + } + } + ] + }, + // 淡季 + { + 'useDate': [ + dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M') + ], + 'unitName': '每人', + 'currency': 'CNY', + 'weekend': [ + '5', + '6' + ], + 'priceList': [ + { + 'priceInput': { + 'numberStart': 1, + 'numberEnd': 2, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 3, + 'numberEnd': 4, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 5, + 'numberEnd': 6, + 'audultPrice': 0, + 'childrenPrice': 0 + } + }, + { + 'priceInput': { + 'numberStart': 7, + 'numberEnd': 9, + 'audultPrice': 0, + 'childrenPrice': 0 + } + } + ] + } + ] +} - setQuotation(sortedData) - setBatchImportPriceVisible(false) +const defaultPriceValue = { + 'priceInput': { + 'numberStart': 1, + 'numberEnd': 2, + 'audultPrice': 0, + 'childrenPrice': 0 } - const quotationTableVisibleOK = () => { - const tempQuotation = [...quotation] +} - const sortedData = [...tempQuotation].sort((a, b) => { - 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 defaultDefinitionValue = { + 'useDate': [ + dayjs().add(1, 'year').subtract(2, 'M').startOf('M'), dayjs().add(1, 'year').endOf('M') + ], + 'unitName': '每人', + 'currency': 'CNY', + 'weekend': [ + '5', + '6' + ], + 'priceList': [ + defaultPriceValue + ] +} - if (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 +const ProductInfoQuotation = ({ editable, ...props }) => { - return aGroupSize - bGroupSize - }) - setQuotation(sortedData) - setQuotationTableVisible(false) - } - const quotationTableVisibleCancel = () => { - setQuotationTableVisible(false) - } + const { t } = useTranslation() - const handleDelete = (index) => { - const newData = [...quotation] - newData.splice(index, 1) - const sortedData = [...newData].sort((a, b) => { - 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)) - if (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 + const [isQuotationModalOpen, setQuotationModalOpen] = useState(false) + const [isBatchSetupModalOpen, setBatchSetupModalOpen] = useState(false) + const [quotationForm] = Form.useForm() + const [batchSetupForm] = Form.useForm() - return aGroupSize - bGroupSize - }) - setQuotation(sortedData) - } + const datePresets = useDatePresets() + + const [newEmptyQuotation, appendQuotationList, quotationList] = + useProductsStore((state) => [state.newEmptyQuotation, state.appendQuotationList, state.quotationList]) + + useEffect(() => { + console.info('quotationList: ', quotationList) + }, [quotationList]) const onQuotationSeleted = async (quotation) => { - // 转换为 RangePicker 赋值格式 + // 把 start, end 转换为 RangePicker 数组格式 quotation.use_dates = [dayjs(quotation.use_dates_start), dayjs(quotation.use_dates_end)] quotationForm.setFieldsValue(quotation) setQuotationModalOpen(true) } const onNewQuotation = () => { - // const emptyQuotation = newEmptyQuotation() - // quotationForm.setFieldsValue(emptyQuotation) - // setQuotationModalOpen(true) + const emptyQuotation = newEmptyQuotation() + quotationForm.setFieldsValue(emptyQuotation) + setQuotationModalOpen(true) } const onQuotationFinish = (values) => { console.info(values) + setQuotationModalOpen(false) // saveOrUpdateQuotation(values) // .then(() => { // setQuotationModalOpen(false) @@ -118,6 +287,35 @@ const ProductInfoQuotation = ({ editable, ...props }) => { // form.resetFields() } + const onBatchSetupFinish = () => { + // console.info(batchSetupForm.getFieldsValue()) + let previewList = [] + const defList = batchSetupForm.getFieldsValue().defList + + defList.forEach(definition => { + const previewPrice = definition?.priceList.map(price => { + return { + id: 0, + 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: definition.unitName, + // 保持和 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(','), + } + }) + previewList.push(...previewPrice) + }) + + appendQuotationList(previewList) + setBatchSetupModalOpen(false) + } + const quotationColumns = [ { title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '4rem' }, { title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '4rem' }, @@ -162,28 +360,110 @@ const ProductInfoQuotation = ({ editable, ...props }) => { <>

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

{ // editable && - } - { // editable && - } - setBatchImportPriceVisible(false)} width={'90%'}> - + onBatchSetupFinish()} + onCancel={() => setBatchSetupModalOpen(false)} + destroyOnClose + forceRender + > +
+ + {(fields, { add, remove }) => ( + + {fields.map((field, index) => ( + : { + remove(field.name) + }} />} + > + + + + + + + + + + + + + + + {(priceFieldList, priceOptList) => ( + + {priceFieldList.map((priceField, index) => ( + + + + + {index == 0 ? : priceOptList.remove(priceField.name)} />} + + ))} + + + )} + + + + ))} + + + )} + + + {() => ( + +
{JSON.stringify(batchSetupForm.getFieldsValue(), null, 2)}
+
+ )} +
+
{ }, ]} > - + { }, ]} > - + { { }, ]} > - - 每人 - 每团 - + + 每人 + 每团 + { }, ]} > - + { }, ]} > - + { }, ]} > - +