From b0d4943f0986d220d0cf7856293d5c8b79f02c98 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 10:18:08 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E6=9C=89=E6=95=88=E6=9C=9F;=20=E6=9C=89?= =?UTF-8?q?=E6=95=88=E7=9A=84=E5=91=A8X;=20style:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 18 ++++++ public/locales/en/products.json | 3 + public/locales/zh/common.json | 18 ++++++ public/locales/zh/products.json | 3 + src/components/SecondHeaderWrapper.jsx | 6 +- src/hooks/useProductsSets.js | 12 ++-- src/views/products/Audit.jsx | 77 ++++++++++++++------------ src/views/products/Index.jsx | 6 +- 8 files changed, 97 insertions(+), 46 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index b113807..dd10cb2 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -54,6 +54,24 @@ "lastThreeMonth": "Last Three Month", "thisYear": "This Year" }, + "weekdays": { + "1": "Monday", + "2": "Tuesday", + "3": "Wednesday", + "4": "Thursday", + "5": "Friday", + "6": "Saturday", + "7": "Sunday" + }, + "weekdaysShort": { + "1": "Mon", + "2": "Tue", + "3": "Wed", + "4": "Thu", + "5": "Fri", + "6": "Sat", + "7": "Sun" + }, "menu": { "Reservation": "Reservation", "Invoice": "Invoice", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index b5e7694..7808b34 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -41,7 +41,10 @@ "Unit": "Unit", "GroupSize": "Group Size", "UseDates": "Use Dates", + "Weekdays": "Weekdays", + "OnWeekdays": "On Weekdays: ", + "Unlimited": "Unlimited", "UseYear": "Use Year", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index a170faa..05a7e38 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -54,6 +54,24 @@ "lastThreeMonth": "前三个月", "thisYear": "今年" }, + "weekdays": { + "1": "一", + "2": "二", + "3": "三", + "4": "四", + "5": "五", + "6": "六", + "7": "日" + }, + "weekdaysShort": { + "1": "一", + "2": "二", + "3": "三", + "4": "四", + "5": "五", + "6": "六", + "7": "日" + }, "menu": { "Reservation": "团预订", "Invoice": "账单", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 9f1f8b6..7eb1c16 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -40,7 +40,10 @@ "Unit": "单位", "GroupSize": "人等", "UseDates": "使用日期", + "Weekdays": "有效日/周X", + "OnWeekdays": "周: ", + "Unlimited": "不限", "UseYear": "年份", diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index 097b0d4..41cec6a 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -1,9 +1,9 @@ import { Outlet, useNavigate } from 'react-router-dom'; -import { Layout, Flex, theme } from 'antd'; +import { Layout, Flex, theme, Spin } from 'antd'; import BackBtn from './BackBtn'; const { Content, Header } = Layout; -const HeaderWrapper = ({ children, header, ...props }) => { +const HeaderWrapper = ({ children, header, loading, ...props }) => { const navigate = useNavigate(); const { token: { colorBgContainer }, @@ -11,6 +11,7 @@ const HeaderWrapper = ({ children, header, ...props }) => { return ( <> +
{/* {header} */} @@ -21,6 +22,7 @@ const HeaderWrapper = ({ children, header, ...props }) => { {children || } + ); diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 6749ede..0dd63f7 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -49,7 +49,7 @@ export const useProductsTypes = () => { { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, { label: t('products:type.Car'), value: 'J', key: 'J' }, { label: t('products:type.Guide'), value: 'Q', key: 'Q' }, - { label: t('products:type.Package'), value: 'D', key: 'D' }, + { label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路 { label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'C', key: 'C' }, { label: t('products:type.Extras'), value: '8', key: '8' }, @@ -67,11 +67,11 @@ export const useProductsAuditStates = () => { useEffect(() => { const newData = [ - { key: '-1', value: '-1', label: t('products:auditState.New') }, - { key: '0', value: '0', label: t('products:auditState.Pending') }, - { key: '2', value: '2', label: t('products:auditState.Approved') }, - { key: '3', value: '3', label: t('products:auditState.Rejected') }, - { key: '1', value: '1', label: t('products:auditState.Published') }, + { key: '-1', value: '-1', label: t('products:auditState.New'), color: 'gray-500' }, + { key: '0', value: '0', label: t('products:auditState.Pending'), color: '' }, + { key: '2', value: '2', label: t('products:auditState.Approved'), color: 'primary' }, + { key: '3', value: '3', label: t('products:auditState.Rejected'), color: 'red-500' }, + { key: '1', value: '1', label: t('products:auditState.Published'), color: 'primary' }, // ELSE 未知 ]; setTypes(newData); diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index e055cc3..4e30330 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,18 +1,20 @@ import { useEffect, useState } from 'react'; -import { useParams, } from 'react-router-dom'; -import { App, Button, Collapse, Table, Space, } from 'antd'; +import { useParams } from 'react-router-dom'; +import { App, Button, Collapse, Table, Space, Divider } from 'antd'; import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; +// import PrintContractPDF from './PrintContractPDF'; -const Header = ({ title, agency, refresh, ...props}) => { +const Header = ({ title, agency, refresh, ...props }) => { + const { use_year, } = useParams(); const { t } = useTranslation(); - const [activeAgency, ] = useProductsStore((state) => [state.activeAgency, ]); + const [activeAgency] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); const handleAuditItem = (state, row) => { - postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) + postProductsQuoteAudit(state, { id: row.id, travel_agency_id: activeAgency.travel_agency_id }) .then((json) => { if (json.errcode === 0) { message.success(json.errmsg); @@ -33,7 +35,7 @@ const Header = ({ title, agency, refresh, ...props}) => { return (
-

{title}

+

{title} {use_year}

{/* */} {/* */} @@ -46,20 +48,21 @@ const Header = ({ title, agency, refresh, ...props}) => { - {/* todo: export */} - + {/* todo: export, 审核完成之后才能导出 */} + + {/* */}
); }; -const PriceTable = ({dataSource,refresh}) => { +const PriceTable = ({ dataSource, refresh }) => { const { t } = useTranslation('products'); - const [loading, activeAgency, ] = useProductsStore((state) => [state.loading, state.activeAgency, ]); + const [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]); const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); const handleAuditPriceItem = (state, row) => { - postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) + postProductsQuoteAudit(state, { id: row.id, travel_agency_id: activeAgency.travel_agency_id }) .then((json) => { if (json.errcode === 0) { message.success(json.errmsg); @@ -79,7 +82,7 @@ const PriceTable = ({dataSource,refresh}) => { }; const columns = [ - { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, + { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, { key: 'adult', title: t('AgeType.Adult'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, { key: 'child', title: t('AgeType.Child'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, // {key: 'price', title: t('Currency'), }, @@ -95,35 +98,37 @@ const PriceTable = ({dataSource,refresh}) => { key: 'useDates', dataIndex: ['use_dates_start'], title: t('UseDates'), - render: (_, { use_dates_start, use_dates_end }) => `${use_dates_start} ~ ${use_dates_end}`, + render: (_, { use_dates_start, use_dates_end, weekdays }) => `${use_dates_start} ~ ${use_dates_end}`, // + (weekdays ? `, ${t('OnWeekdays')}${weekdays}` : ''), }, - { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays') }, + { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays'), render: (text, r) => text || t('Unlimited') }, { key: 'state', title: t('State'), render: (_, r) => { - return stateMapVal[`${r.audit_state_id}`]?.label; + return {stateMapVal[`${r.audit_state_id}`]?.label}; }, }, { title: '价格审核', key: 'action', - render: (_, r) => r.audit_state_id <= 0 ?( - - - - - ) : null, + render: (_, r) => + r.audit_state_id <= 0 ? ( + + + + + ) : null, }, ]; - return r.id} />; -} + return
r.id} />; +}; /** * */ const TypesPanels = (props) => { - const [loading, agencyProducts, ] = useProductsStore((state) => [state.loading, state.agencyProducts]); + const { t } = useTranslation(); + const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); // console.log(agencyProducts); const productsTypes = useProductsTypes(); const [activeKey, setActiveKey] = useState([]); @@ -138,7 +143,7 @@ const TypesPanels = (props) => { children: ( r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} + dataSource={agencyProducts[ele.value].reduce((r, c) => r.concat(c.quotation.map((q, i) => ({ ...q, weekdays: q.weekdays.split(',').filter(Boolean).map(w => t(`weekdaysShort.${w}`)).join(', '), info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} refresh={props.refresh} /> ), @@ -151,31 +156,33 @@ const TypesPanels = (props) => { }, [productsTypes, agencyProducts]); const onCollapseChange = (_activeKey) => { - setActiveKey(_activeKey) - } - return ( - - ) -} + setActiveKey(_activeKey); + }; + return ; +}; const Audit = ({ ...props }) => { const { travel_agency_id, use_year, audit_state } = useParams(); - const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); + const [loading, activeAgency, getAgencyProducts] = useProductsStore((state) => [state.loading, state.activeAgency, state.getAgencyProducts]); const handleGetAgencyProducts = () => { getAgencyProducts({ travel_agency_id, use_year, audit_state }); - } + }; useEffect(() => { handleGetAgencyProducts(); return () => {}; - }, [travel_agency_id]) + }, [travel_agency_id]); return ( <> - }> + } loading={loading} >
+ {/* */} + {/* debug: 0 */} + {/* */} +
diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index d1b1495..400db96 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -16,7 +16,7 @@ function Index() { const handleSearchAgency = (formVal = undefined) => { const { starttime, endtime, ...param } = formVal || formValuesToSub; - const searchParam = objectMapper(param, { agency: 'travel_agency_ids', startdate: 'edit_date1', enddate: 'edit_date2' }); + const searchParam = objectMapper(param, { agency: 'travel_agency_ids', startdate: 'edit_date1', enddate: 'edit_date2', year: 'use_year' }); setSearchValues(searchParam); searchAgency(searchParam); } @@ -39,8 +39,8 @@ function Index() { key: 'action', render: (_, r) => ( - {t('Edit')} - {t('Audit')} + {t('Edit')} + {t('Audit')} ), }, From 079b74c3b4a58aae0045803f33905f90a0e81236 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 10:54:14 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 8 ++++---- src/views/products/{Index.jsx => Manage.jsx} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename src/views/products/{Index.jsx => Manage.jsx} (100%) diff --git a/src/main.jsx b/src/main.jsx index bd73f53..6df42c7 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -32,11 +32,11 @@ import Airticket from "@/views/airticket/Index"; import AirticketPlan from "@/views/airticket/Plan"; import { ThemeContext } from '@/stores/ThemeContext' -import ProductsIndex from '@/views/products/Index'; +import ProductsManage from '@/views/products/Manage'; import ProductsDetail from '@/views/products/Detail'; import ProductsAudit from '@/views/products/Audit'; import Gysgl from "@/views/gysgl/Index" -import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } from '@/config' +import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/config' import './i18n'; @@ -65,8 +65,8 @@ const router = createBrowserRouter([ { path: "invoice/paid/detail/:flid", element: }, { path: "airticket",element: }, { path: "airticket/plan/:coli_sn",element:}, - { path: "products",element:}, - { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:}, + { path: "products",element: }, + { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:}, { path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:}, { path: "gysgl",element:}, ] diff --git a/src/views/products/Index.jsx b/src/views/products/Manage.jsx similarity index 100% rename from src/views/products/Index.jsx rename to src/views/products/Manage.jsx From 7409ec1c16215b9d1d28d14e023caf904c90d78f Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 11:21:16 +0800 Subject: [PATCH 03/10] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=9A=84=E9=99=84=E5=8A=A0=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Products/Index.js | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index a18f098..5ce07d3 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -15,6 +15,16 @@ export const getAgencyProductsAction = async (param) => { return errcode !== 0 ? { agency: {}, products: [] } : result; }; +/** + * 获取指定产品的附加项目 + * @param {object} param { id, travel_agency_id, use_year } + */ +export const getAgencyProductExtrasAction = async (param) => { + const _param = { ...param, use_year: (param.use_year || '').replace('all', '') }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_extras`, _param); + return errcode !== 0 ? [] : result; +}; + export const postProductsQuoteAudit = async (auditState, quoteRow) => { const postbody = { audit_state: auditState, @@ -59,11 +69,11 @@ export const useProductsStore = create( ...initialState, // state actions - setLoading: loading => set({ loading }), - setSearchValues: searchValues => set({ searchValues }), + setLoading: (loading) => set({ loading }), + setSearchValues: (searchValues) => set({ searchValues }), setAgencyList: (agencyList) => set({ agencyList }), - setActiveAgency: activeAgency => set({ activeAgency }), - setAgencyProducts: agencyProducts => set({ agencyProducts }), + setActiveAgency: (activeAgency) => set({ activeAgency }), + setAgencyProducts: (agencyProducts) => set({ agencyProducts }), reset: () => set(initialState), @@ -80,12 +90,16 @@ export const useProductsStore = create( const { setLoading, setActiveAgency, setAgencyProducts } = get(); setLoading(true); const res = await getAgencyProductsAction(param); - const productsData = groupBy(res.products, row => row.info.product_type_id); + const productsData = groupBy(res.products, (row) => row.info.product_type_id); setAgencyProducts(productsData); setActiveAgency(res.agency); setLoading(false); }, + getAgencyProductExtras: async (param) => { + const res = await getAgencyProductExtrasAction(param); + // todo: + }, })) ); export default useProductsStore; From 3c533c96b41316a6e1f6a6ab9e0bb3de33bbb188 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 13:46:31 +0800 Subject: [PATCH 04/10] style: --- src/components/SecondHeaderWrapper.jsx | 5 +++-- src/views/products/Audit.jsx | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index 41cec6a..9727e4a 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -1,5 +1,5 @@ import { Outlet, useNavigate } from 'react-router-dom'; -import { Layout, Flex, theme, Spin } from 'antd'; +import { Layout, Flex, theme, Spin, Divider } from 'antd'; import BackBtn from './BackBtn'; const { Content, Header } = Layout; @@ -10,7 +10,7 @@ const HeaderWrapper = ({ children, header, loading, ...props }) => { } = theme.useToken(); return ( <> - +
@@ -19,6 +19,7 @@ const HeaderWrapper = ({ children, header, loading, ...props }) => {
+
{children || } diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 4e30330..9a8371b 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -35,7 +35,7 @@ const Header = ({ title, agency, refresh, ...props }) => { return (
-

{title} {use_year}

+

{title}{use_year}

{/* */} {/* */} @@ -178,8 +178,6 @@ const Audit = ({ ...props }) => { return ( <> } loading={loading} > -
- {/* */} {/* debug: 0 */} {/* */} From 1b97cb41ddf8058076a6d77c7a38e9371a016bac Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 14:06:57 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=86=B2=E7=AA=81?= =?UTF-8?q?=E5=92=8C=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 3 +++ public/locales/en/products.json | 17 ++++++------ public/locales/zh/common.json | 3 +++ public/locales/zh/products.json | 47 ++++++++++++++++----------------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index dd10cb2..b8dc2bb 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -25,6 +25,9 @@ "Export": "Export", "Copy": "Copy", + "sureCancel": "Sure you want to cancel?", + "sureDelete":"Sure you want to delete?", + "Table": { "Total": "Total {{total}} items" }, diff --git a/public/locales/en/products.json b/public/locales/en/products.json index cca7397..51e8eb9 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -34,7 +34,7 @@ "CreateDate": "Create Date", "AuditedBy": "Audited By", "AuditDate": "Audit Date", - + "productProject": "Product project", "Code": "Code", "City": "City", @@ -57,14 +57,6 @@ "operation": "Operation", "price": "Price", - - "save":"save", - "edit":"edit", - "delete":"delete", - "cancel":"cancel", - "sureCancel":"Sure you want to cancel?", - "sureDelete":"Sure you want to delete?" - "Quotation": "Quotation", "Offer": "Offer", @@ -84,5 +76,12 @@ "Child": "Child" }, + "save":"save", + "edit":"edit", + "delete":"delete", + "cancel":"cancel", + "sureCancel":"Sure you want to cancel?", + "sureDelete":"Sure you want to delete?", + "#": "#" } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 80ceafb..547bd74 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -25,6 +25,9 @@ "Export": "导出", "Copy": "复制", + "sureCancel": "确定取消?", + "sureDelete":"确定删除?", + "Table": { "Total": "共 {{total}} 条" }, diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 6c3b6b9..8a336cc 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -37,28 +37,6 @@ "AuditedBy": "审核人员", "AuditDate": "审核时间", - "Quotation": "报价", - "Offer": "报价", - "Unit": "单位", - "GroupSize": "人等", - "UseDates": "使用日期", - - "Weekdays": "有效日/周X", - "OnWeekdays": "周: ", - "Unlimited": "不限", - - "UseYear": "年份", - - "AgeType": { - "Type": "人群", - "Adult": "成人", - "Child": "儿童" - }, - - "#": "#" - "Auditors": "审核人员", - "AuditDate": "审核时间", - "productProject": "产品项目", "Code": "代码", "City": "城市", @@ -81,10 +59,31 @@ "validityPeriod":"有效期", "operation": "操作", "price": "价格", + + "Quotation": "报价", + "Offer": "报价", + "Unit": "单位", + "GroupSize": "人等", + "UseDates": "使用日期", + + "Weekdays": "有效日/周X", + "OnWeekdays": "周: ", + "Unlimited": "不限", + + "UseYear": "年份", + + "AgeType": { + "Type": "人群", + "Adult": "成人", + "Child": "儿童" + }, + "save":"保存", "edit":"编辑", - "cancel":"取消", "delete":"删除", + "cancel":"取消", "sureCancel": "确定取消?", - "sureDelete":"确定删除?" + "sureDelete":"确定删除?", + + "#": "#" } From 2bdf22c1408381c5084de4559a81837adf92d28a Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 16:48:38 +0800 Subject: [PATCH 06/10] =?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; From 056a0068473129fd2cf75ed87c07f35054e81c64 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 17:13:45 +0800 Subject: [PATCH 07/10] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B7=B3=E8=BD=AC`=E7=BC=96=E8=BE=91`;=20sty?= =?UTF-8?q?le:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SecondHeaderWrapper.jsx | 30 +++++++++++++------------- src/views/products/Audit.jsx | 7 +++--- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index 9727e4a..9a4f7bd 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -10,21 +10,21 @@ const HeaderWrapper = ({ children, header, loading, ...props }) => { } = theme.useToken(); return ( <> - - -
- - {/* {header} */} -
{header}
- -
-
-
- - {children || } - -
-
+ + +
+ + {/* {header} */} +
{header}
+ +
+
+ + + {children || } + +
+
); }; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 9a8371b..44fb12c 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { useParams } from 'react-router-dom'; +import { useParams, Link } from 'react-router-dom'; import { App, Button, Collapse, Table, Space, Divider } from 'antd'; import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; @@ -9,7 +9,7 @@ import { isEmpty } from '@/utils/commons'; // import PrintContractPDF from './PrintContractPDF'; const Header = ({ title, agency, refresh, ...props }) => { - const { use_year, } = useParams(); + const { travel_agency_id, use_year, audit_state } = useParams(); const { t } = useTranslation(); const [activeAgency] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); @@ -35,10 +35,11 @@ const Header = ({ title, agency, refresh, ...props }) => { return (
-

{title}{use_year}

+

{title}{(use_year || '').replace('all', '')}

{/* */} {/* */} + {t('Edit')} From e4cc07eefea83d88b832719296e266d063a2a664 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 09:14:35 +0800 Subject: [PATCH 08/10] =?UTF-8?q?feat:=20=E9=99=84=E5=8A=A0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE:=20=E6=90=9C=E7=B4=A2=E4=BE=9B=E5=BA=94=E5=95=86?= =?UTF-8?q?=E7=9A=84=E4=BA=A7=E5=93=81,=20=E5=B7=B2=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail/Extras.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index 0aa1aa2..ebb6e2d 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -24,7 +24,7 @@ const NewAddonModal = ({ onPick, ...props }) => { const { starttime, endtime, ...param } = copyObject; setSearchLoading(true); setSearchResult([]); - const result = await getAgencyProductsAction({ ...param, audit_state: '0', travel_agency_id, use_year }); + const result = await getAgencyProductsAction({ ...param, audit_state: '1', travel_agency_id, use_year }); setSearchResult(result?.products || []); setSearchLoading(false); }; From 65064937f07d29d60ac795bab1664e33c63144cf Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 10:39:52 +0800 Subject: [PATCH 09/10] =?UTF-8?q?feat:=20=E5=A4=8D=E5=88=B6=E4=BE=9B?= =?UTF-8?q?=E5=BA=94=E5=95=86=E4=BA=A7=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 4 +- public/locales/zh/products.json | 2 +- src/components/SearchForm.jsx | 7 ++-- src/stores/Products/Index.js | 15 ++++++- src/views/products/Audit.jsx | 6 +-- src/views/products/Detail/Extras.jsx | 4 +- src/views/products/Manage.jsx | 63 ++++++++++++++++++++-------- 7 files changed, 70 insertions(+), 31 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 56eab4b..4997e31 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -3,14 +3,14 @@ "Experience": "Experience", "Car": "Transport Services", "Guide": "Guide Services", - "Package": "Package", + "Package": "Package Tour", "Attractions": "Attractions", "Meals": "Meals", "Extras": "Extras", "Overtravel": "超公里", "Special": "Special" }, - "Components": { + "EditComponents": { "info": "Product Information", "Quotation": "Quotation", "Extras": "Add-on" diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 08f4872..55005ce 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -10,7 +10,7 @@ "Overtravel": "超公里", "Special": "特殊项目" }, - "Components": { + "EditComponents": { "info": "产品信息", "Quotation": "报价", "Extras": "附加项目" diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 979a6fc..f6bad2c 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -21,7 +21,7 @@ export const fetchVendorList = async (q) => { const { RangePicker } = DatePicker; -const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { +const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, loading, ...props }) => { const { t } = useTranslation(); const presets = useDatePresets(); const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]); @@ -36,7 +36,6 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { shows: [], ...props.fieldsConfig, }; - const { confirmText } = props; const readValues = { ...initialValue, ...formValues }; const formValuesMapper = (values) => { @@ -109,14 +108,14 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { }; return ( <> -
+ {/* */} {getFields({ sort, initialValue: readValues, hides, shows, fieldProps, fieldComProps, form, presets, t })} {/* 'textAlign': 'right' */}
- {/*
r.info.id} /> diff --git a/src/views/products/Manage.jsx b/src/views/products/Manage.jsx index 400db96..e18b164 100644 --- a/src/views/products/Manage.jsx +++ b/src/views/products/Manage.jsx @@ -1,14 +1,16 @@ -import { useEffect } from 'react'; -import { Link } from 'react-router-dom'; -import { Row, Col, Space, Table } from 'antd'; +import { useEffect, useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { App, Space, Table, Button, Modal } from 'antd'; import SearchForm from '@/components/SearchForm'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; -import useProductsStore from '@/stores/Products/Index'; +import useProductsStore, { copyAgencyDataAction } from '@/stores/Products/Index'; import useFormStore from '@/stores/Form'; import { objectMapper } from '@/utils/commons'; function Index() { + const { notification, message } = App.useApp(); + const navigate = useNavigate() const { t } = useTranslation(); const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]); const [searchValues, setSearchValues] = useProductsStore((state) => [state.searchValues, state.setSearchValues]); @@ -21,6 +23,24 @@ function Index() { searchAgency(searchParam); } + const [copyModalVisible, setCopyModalVisible] = useState(false); + const [sourceAgency, setSourceAgency] = useState({}); + const [copyLoading, setCopyLoading] = useState(false); + const handleCopyAgency = async (toID) => { + setCopyLoading(true); + const success = await copyAgencyDataAction(sourceAgency.travel_agency_id, toID); + setCopyLoading(false); + success ? message.success('复制成功') : message.error('复制失败'); + + setCopyModalVisible(false); + navigate(`/products/${toID}/${searchValues.use_year || 'all'}/${searchValues.audit_state || 'all'}/edit`); + }; + + const openCopyModal = (from) => { + setSourceAgency(from); + setCopyModalVisible(true); + }; + useEffect(() => { // handleSearchAgency(); }, []); @@ -41,6 +61,7 @@ function Index() { {t('Edit')} {t('Audit')} + ), }, @@ -69,19 +90,27 @@ function Index() { handleSearchAgency(formVal); }} /> - - -
- - - +
+ + {/* 复制弹窗 */} + setCopyModalVisible(false)} destroyOnClose> +
复制源: {sourceAgency.travel_agency_name}
+ { + handleCopyAgency(formVal.agency); + }} + /> +
); } From cbdb737163cdce9402f86cbccd6835e67858cf08 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 10:51:50 +0800 Subject: [PATCH 10/10] =?UTF-8?q?feat:=20=E5=AE=A2=E6=9C=8D=E9=A6=96?= =?UTF-8?q?=E9=A1=B5:=20=E6=90=9C=E7=B4=A2:=20=E4=BE=9B=E5=BA=94=E5=95=86?= =?UTF-8?q?=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SearchInput.jsx | 2 +- src/views/products/Manage.jsx | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/SearchInput.jsx b/src/components/SearchInput.jsx index dcf2657..134c301 100644 --- a/src/components/SearchInput.jsx +++ b/src/components/SearchInput.jsx @@ -31,7 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) { showSearch allowClear maxTagCount={1} - dropdownStyle={{width: '16rem'}} + dropdownStyle={{width: '20rem'}} {...props} onSearch={debounceFetcher} notFoundContent={fetching ? : null} diff --git a/src/views/products/Manage.jsx b/src/views/products/Manage.jsx index e18b164..3e5d3b6 100644 --- a/src/views/products/Manage.jsx +++ b/src/views/products/Manage.jsx @@ -70,15 +70,12 @@ function Index() {