From 2bdf22c1408381c5084de4559a81837adf92d28a Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 16:48:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81:=20=E9=99=84?= =?UTF-8?q?=E5=8A=A0=E9=A1=B9=E7=9B=AE:=20=E6=96=B0=E5=A2=9E;=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4;=20=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 4 + public/locales/en/products.json | 5 + public/locales/zh/common.json | 4 + public/locales/zh/products.json | 5 + src/stores/Products/Index.js | 18 ++- src/utils/request.js | 8 +- src/views/products/Detail.jsx | 6 +- src/views/products/Detail/Extras.jsx | 173 +++++++++++++++++++++++++++ 8 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 src/views/products/Detail/Extras.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index b8dc2bb..8ed1854 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -10,6 +10,7 @@ "Confirm": "Confirm", "Close": "Close", "Save": "Save", + "New": "New", "Edit": "Edit", "Audit": "Audit", "Delete": "Delete", @@ -28,6 +29,9 @@ "sureCancel": "Sure you want to cancel?", "sureDelete":"Sure you want to delete?", + "Success": "Success", + "Failed": "Failed", + "Table": { "Total": "Total {{total}} items" }, diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 51e8eb9..56eab4b 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -10,6 +10,11 @@ "Overtravel": "超公里", "Special": "Special" }, + "Components": { + "info": "Product Information", + "Quotation": "Quotation", + "Extras": "Add-on" + }, "auditState": { "New": "New", "Pending": "Pending", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 547bd74..82b43f9 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -10,6 +10,7 @@ "Confirm": "确认", "Close": "关闭", "Save": "保存", + "New": "新增", "Edit": "编辑", "Audit": "审核", "Delete": "删除", @@ -28,6 +29,9 @@ "sureCancel": "确定取消?", "sureDelete":"确定删除?", + "Success": "成功", + "Failed": "失败", + "Table": { "Total": "共 {{total}} 条" }, diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 8a336cc..08f4872 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -10,6 +10,11 @@ "Overtravel": "超公里", "Special": "特殊项目" }, + "Components": { + "info": "产品信息", + "Quotation": "报价", + "Extras": "附加项目" + }, "auditState": { "New": "新增", "Pending": "待审核", diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 5ce07d3..e012aab 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; -import { fetchJSON, postForm } from '@/utils/request'; +import { fetchJSON, postForm, postJSON } from '@/utils/request'; import { HT_HOST } from '@/config'; import { groupBy } from '@/utils/commons'; @@ -15,6 +15,22 @@ export const getAgencyProductsAction = async (param) => { return errcode !== 0 ? { agency: {}, products: [] } : result; }; +/** + * todo: + */ +export const addProductExtraAction = async (body) => { + const { errcode, result } = await postJSON(`${HT_HOST}/products/extras`, body); + return errcode === 0 ? true : false; +}; + +/** + * todo: + */ +export const delProductExtrasAction = async (body) => { + const { errcode, result } = await postJSON(`${HT_HOST}/products/extras/del`, body); + return errcode === 0 ? true : false; +}; + /** * 获取指定产品的附加项目 * @param {object} param { id, travel_agency_id, use_year } diff --git a/src/utils/request.js b/src/utils/request.js index ac97803..b99451d 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -113,8 +113,14 @@ export function postForm(url, data) { } export function postJSON(url, obj) { + const initParams = getRequestInitParams(); + const params4get = Object.assign({}, initParams); + const params = new URLSearchParams(params4get).toString(); + const ifp = url.includes('?') ? '&' : '?'; + const fUrl = params !== '' ? `${url}${ifp}${params}` : url; + const headerObj = getRequestHeader() - return fetch(url, { + return fetch(fUrl, { method: 'POST', body: JSON.stringify(obj), headers: { diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 7188431..f8335d8 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next'; import DateComponent from '@/components/date'; import { fetchJSON } from "@/utils/request"; +import Extras from './Detail/Extras'; function Index() { @@ -561,7 +562,7 @@ function Index() { -

{t('products:bindingProducts')}

+ {/*

{t('products:bindingProducts')}

{t('products:addBinding')} - + */} + diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx new file mode 100644 index 0000000..0aa1aa2 --- /dev/null +++ b/src/views/products/Detail/Extras.jsx @@ -0,0 +1,173 @@ +import { createContext, useContext, useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { App, Table, Button, Modal, Popconfirm } from 'antd'; +import useProductsStore, { postProductsQuoteAudit, getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index'; +import { isEmpty, cloneDeep } from '@/utils/commons'; +import SearchForm from '@/components/SearchForm'; + +import RequireAuth from '@/components/RequireAuth'; +import { PERM_PRODUCTS_MANAGEMENT } from '@/config'; + +const NewAddonModal = ({ onPick, ...props }) => { + const { travel_agency_id, use_year } = useParams(); + const { t } = useTranslation(); + const { notification, message } = App.useApp(); + + const [open, setOpen] = useState(false); + const [loading, setLoading] = useState(false); // bind loading + const [searchLoading, setSearchLoading] = useState(false); + const [searchResult, setSearchResult] = useState([]); + + const onSearchProducts = async (values) => { + const copyObject = cloneDeep(values); + const { starttime, endtime, ...param } = copyObject; + setSearchLoading(true); + setSearchResult([]); + const result = await getAgencyProductsAction({ ...param, audit_state: '0', travel_agency_id, use_year }); + setSearchResult(result?.products || []); + setSearchLoading(false); + }; + const handleAddExtras = async (item) => { + // const success = await fetchBindOrder({ coli_sn, conversationid: currentConversationID }); + // success ? message.success('绑定成功') : message.error('绑定失败'); + // setOpen(false); + if (typeof onPick === 'function') { + onPick(item); + } + }; + + // todo: + const searchResultColumns = [ + { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('products:Title') }, + { + title: t('products:price'), + dataIndex: ['quotation', '0', 'value'], + width: '10rem', + render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 + }, + { + key: 'action', + title: '', + width: 150, + render: (_, record) => ( + + ), + }, + ]; + const paginationProps = { + showTotal: (total) => t('Table.Total', { total }), + }; + return ( + <> + + + setOpen(false)} destroyOnClose> + { + onSearchProducts(formVal); + }} + /> +
+ + + ); +}; + +/** + * + */ +const Extras = ({ productId, onChange, ...props }) => { + const { t } = useTranslation(); + const { notification, message } = App.useApp(); + + const { travel_agency_id, use_year } = useParams(); + + const [extrasData, setExtrasData] = useState([]); + + const handleGetAgencyProductExtras = async () => { + const data = await getAgencyProductExtrasAction({ id: productId, travel_agency_id, use_year }); + setExtrasData(data); + }; + + const handleNewAddOn = async (item) => { + setExtrasData(prev => [].concat(prev, [item])); + // todo: 提交后端; 重复绑定同一个 + const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [2] }); + newSuccess ? message.success(t('Success')) : message.error(t('Failed')); + await handleGetAgencyProductExtras(); + } + + const handleDelAddon = async (item) => { + // todo: 提交后端 + const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [2] }); + delSuccess ? message.success(t('Success')) : message.error(t('Failed')); + await handleGetAgencyProductExtras(); + }; + + useEffect(() => { + handleGetAgencyProductExtras(); + + return () => {}; + }, []); + + const columns = [ + { title: t('products:Title'), dataIndex: ['info', 'title'], width: '16rem', }, + { + title: t('products:Offer'), + dataIndex: ['quotation', '0', 'value'], + width: '10rem', + + render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 + }, + // { title: t('products:Types'), dataIndex: 'age_type', width: '40%', }, + { + title: '', + dataIndex: 'operation', + width: '4rem', + render: (_, r) => ( + handleDelAddon(r)} > + + + ), + }, + ]; + + return ( + <> + +

{t('products:Components.Extras')}

+
r.info.id} /> + + + + ); +}; +export default Extras;