From 1142a1a893f89d3112512ee3415134b280e180ef Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 17 Jun 2024 11:52:10 +0800 Subject: [PATCH 01/46] =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E4=BA=A7=E5=93=81=E9=A1=B5=E9=9D=A2?= 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 | 26 +++++++++++++ public/locales/zh/common.json | 3 +- public/locales/zh/products.json | 26 +++++++++++++ src/components/SearchForm.jsx | 66 ++++++++++++++++++++++++++++++--- src/components/SearchInput.jsx | 48 ++++++++++++++++++++++++ src/hooks/useProductsSets.js | 46 +++++++++++++++++++++++ src/i18n/index.js | 2 +- src/main.jsx | 2 + src/stores/Products/Index.js | 24 ++++++++++++ src/views/App.jsx | 2 + src/views/invoice/Index.jsx | 2 +- src/views/products/Index.jsx | 55 +++++++++++++++++++++++++++ 13 files changed, 296 insertions(+), 9 deletions(-) create mode 100644 public/locales/en/products.json create mode 100644 public/locales/zh/products.json create mode 100644 src/components/SearchInput.jsx create mode 100644 src/hooks/useProductsSets.js create mode 100644 src/stores/Products/Index.js create mode 100644 src/views/products/Index.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index a513e2a..4575ad3 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -51,7 +51,8 @@ "Feedback": "Feedback", "Notice": "Notice", "Report": "Report", - "Airticket": "AirTicket" + "Airticket": "AirTicket", + "Products": "Products" }, "Validation": { "Title": "Notification", diff --git a/public/locales/en/products.json b/public/locales/en/products.json new file mode 100644 index 0000000..4813a31 --- /dev/null +++ b/public/locales/en/products.json @@ -0,0 +1,26 @@ +{ + "type": { + "Experience": "Experience", + "Car": "Transport Services", + "Guide": "Guide Services", + "Package": "Package", + "Attractions": "Attractions", + "Meals": "Meals", + "Extras": "Extras", + "Special": "Special" + }, + "auditState": { + "New": "New", + "Pending": "Pending", + "Approve": "Approve", + "Rejected": "Rejected", + "Published": "Published" + }, + "Title": "Title", + "Vendor": "Vendor", + "AuState": "Audit State", + "CreatedBy": "Created By", + "CreateDate": "Create Date", + "Auditors": "Auditors", + "AuditDate": "Audit Date" +} diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 59c417e..d689719 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -51,7 +51,8 @@ "Feedback": "反馈表", "Notice": "通知", "Report": "质量评分", - "Airticket": "机票订票" + "Airticket": "机票订票", + "Products": "产品管理" }, "Validation": { "Title": "温馨提示", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json new file mode 100644 index 0000000..326bd78 --- /dev/null +++ b/public/locales/zh/products.json @@ -0,0 +1,26 @@ +{ + "type": { + "Experience": "综费", + "Car": "车费", + "Guide": "导服", + "Package": "包价线路", + "Attractions": "景点", + "Meals": "餐费", + "Extras": "附加", + "Special": "特殊项目" + }, + "auditState": { + "New": "新增", + "Pending": "待审核", + "Approve": "已通过", + "Rejected": "已拒绝", + "Published": "已发布" + }, + "Title": "名称", + "Vendor": "供应商", + "AuState": "审核状态", + "CreatedBy": "提交人员", + "CreateDate": "提交时间", + "Auditors": "审核人员", + "AuditDate": "审核时间" +} diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 0e33c82..6ccbeae 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -5,6 +5,16 @@ import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config'; import useFormStore from '@/stores/Form'; import usePresets from '@/hooks/usePresets'; import { useTranslation } from 'react-i18next'; +import SearchInput from './SearchInput'; +import { fetchJSON } from '@/utils/request'; +import { HT_HOST } from '@/config'; + + +//供应商列表 +export const fetchVendorList = async () => { + const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-cusservice/PTGetHWVendorList`) + return errcode !== 0 ? [] : Result +} const { RangePicker } = DatePicker; @@ -28,6 +38,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { const formValuesMapper = (values) => { const destinationObject = { + 'keyword': { key: 'keyword', transform: (value) => value || '' }, 'referenceNo': { key: 'referenceNo', transform: (value) => value || '' }, 'dates': [ { key: 'startdate', transform: (arrVal) => (arrVal ? arrVal[0].format(DATE_FORMAT) : '') }, @@ -36,6 +47,13 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { { key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') }, ], 'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' }, + 'auditStatus': { key: 'auditStatus', transform: (value) => value?.value || value?.key || '', default: '' }, + 'agency': { + key: 'agency', + transform: (value) => { + return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : ''; + }, + }, }; let dest = {}; const { dates, ...omittedValue } = values; @@ -57,9 +75,9 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { }, []); const onFinish = (values) => { - console.log('Received values of form, origin form value: ', values); + console.log('Received values of form, origin form value: \n', values); const dest = formValuesMapper(values); - console.log('form value send to onSubmit:', dest); + console.log('form value send to onSubmit:\n', dest); const str = new URLSearchParams(dest).toString(); setFormValues(values); setFormValuesToSub(dest); @@ -132,17 +150,25 @@ function getFields(props) { }; let baseChildren = []; baseChildren = [ + item( + 'keyword', + 99, + + + , + fieldProps?.keyword?.col || 6 + ), item( 'referenceNo', - 1, + 99, , - fieldProps?.referenceNo?.col || 4 + fieldProps?.referenceNo?.col || 6 ), item( 'PNR', - 2, + 99, , @@ -176,6 +202,36 @@ function getFields(props) { , fieldProps?.dates?.col || midCol ), + /** + * + */ + item( + 'agency', + 99, + + + , + fieldProps?.agency?.col || 6 + ), + item( + 'auditState', + 99, + + : null} + optionFilterProp='label' + > + {options.map((d) => ( + + {d.label} + + ))} + + ); +} + +export default DebounceSelect; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js new file mode 100644 index 0000000..0834d52 --- /dev/null +++ b/src/hooks/useProductsSets.js @@ -0,0 +1,46 @@ +import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +/** + * 产品管理 相关的预设数据 + */ + +export const useProductsTypes = () => { + const [types, setTypes] = useState([]); + const { t, i18n } = useTranslation(); + + useEffect(() => { + const newData = [ + { label: t('products:type.Experience'), value: t('products:type.Experience') }, + {label: t("products:type.Car"), value: t("products:type.Car"),}, + {label: t("products:type.Guide"), value: t("products:type.Guide"),}, + {label: t("products:type.Package"), value: t("products:type.Package"),}, + {label: t("products:type.Attractions"), value: t("products:type.Attractions"),}, + {label: t("products:type.Meals"), value: t("products:type.Meals"),}, + {label: t("products:type.Extras"), value: t("products:type.Extras"),}, + {label: t("products:type.Special"), value: t("products:type.Special")}, + ]; + setTypes(newData); + }, [i18n.language]); + + return types; +}; + + +export const useProductsAuditStatus = () => { + const [types, setTypes] = useState([]); + const { t, i18n } = useTranslation(); + + useEffect(() => { + const newData = [ + { value: '0', label: 'New' }, + { value: '1', label: 'Pending' }, + { value: '2', label: 'Approve' }, + { value: '3', label: 'Rejected' }, + { value: '4', label: 'Published' }, + ]; + setTypes(newData); + }, [i18n.language]); + + return types; +}; diff --git a/src/i18n/index.js b/src/i18n/index.js index 9efd3bd..edd3f75 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -17,7 +17,7 @@ i18n backend: { loadPath: '/locales/{{lng}}/{{ns}}.json', }, - ns: ['common', 'group', 'vendor', 'account'], + ns: ['common', 'group', 'vendor', 'account', 'products'], defaultNS: 'common', detection: { // convertDetectedLanguage: 'Iso15897', diff --git a/src/main.jsx b/src/main.jsx index ff392ac..0f7c4fc 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -30,6 +30,7 @@ import InvoiceDetail from "@/views/invoice/Detail"; import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; +import ProductsIndex from '@/views/products/Index'; import './i18n'; @@ -65,6 +66,7 @@ const router = createBrowserRouter([ { path: "invoice/paid",element:}, { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, + { path: "products",element:}, ] }, { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js new file mode 100644 index 0000000..87066a9 --- /dev/null +++ b/src/stores/Products/Index.js @@ -0,0 +1,24 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; + +import { fetchJSON } from '@/utils/request'; +import { HT_HOST } from '@/config'; + +const initialState = { + loading: false, + productsList: [], +}; +export const useProductsStore = create( + devtools((set, get) => ({ + // 初始化状态 + ...initialState, + + // state actions + setProductsList: (productsList) => set({ productsList }), + + reset: () => set(initialState), + + // side effects + })) +); +export default useProductsStore; diff --git a/src/views/App.jsx b/src/views/App.jsx index dd2dec6..cbb2949 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -125,6 +125,7 @@ function App() { { key: 'feedback', label: {t('menu.Feedback')} }, { key: 'report', label: {t('menu.Report')} }, { key: 'airticket', label: {t('menu.Airticket')} }, + { key: 'products', label: {t('menu.Products')} }, { key: 'notice', label: ( @@ -151,6 +152,7 @@ function App() { { label: {t('account:management.tile')}, key: '3' }, { type: 'divider' }, { label: {t('Logout')}, key: '4' }, + { label: {t('ChangeVendor')}, key: 'change-vendor' }, ], { type: 'divider' }, { label: <>v{BUILD_VERSION}, key: 'BUILD_VERSION' }, diff --git a/src/views/invoice/Index.jsx b/src/views/invoice/Index.jsx index 02033ed..17a9603 100644 --- a/src/views/invoice/Index.jsx +++ b/src/views/invoice/Index.jsx @@ -82,7 +82,7 @@ function Index() { fieldsConfig={{ shows: ['referenceNo', 'invoiceStatus', 'dates'], fieldProps: { - referenceNo: { col: 5 }, + referenceNo: { col: 7 }, invoiceStatus: { col: 4}, dates: { col: 10 }, }, diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx new file mode 100644 index 0000000..e75ac50 --- /dev/null +++ b/src/views/products/Index.jsx @@ -0,0 +1,55 @@ +import { useEffect, useState } from 'react'; +import { Row, Col, Space, Typography, Table, Button } from 'antd'; +import useProductsStore from '@/stores/Products/Index'; +import { usingStorage } from '@/hooks/usingStorage'; +import SearchForm from '@/components/SearchForm'; +import dayjs from 'dayjs'; +import { useTranslation } from 'react-i18next'; +import { useProductsTypes } from '@/hooks/useProductsSets'; + +function Index() { + const { t } = useTranslation(); + const { userId } = usingStorage(); + const [loading, productsList] = useProductsStore((state) => state.productsList); + + const [noticeList, setNoticeList] = useState([]); + useEffect(() => {}, []); + + const showTotal = (total) => `Total ${total} items`; + const columns = [ + { title: t('products:Vendor'), key: '', dataIndex: '' }, + { title: t('products:AuState'), key: '', dataIndex: '' }, + { title: t('products:CreatedBy'), key: '', dataIndex: '' }, + { title: t('products:CreateDate'), key: '', dataIndex: '' }, + { title: t('products:Auditors'), key: '', dataIndex: '' }, + { title: t('products:AuditDate'), key: '', dataIndex: '' }, + { title: '', key: 'action', render: () => }, + ]; + return ( + + { + }} + /> + + + + + + + + ); +} + +export default Index; From fa0550dfdb324b583fd941c4c16eb2c3ba4cc655 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 17 Jun 2024 17:00:53 +0800 Subject: [PATCH 02/46] =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 2 ++ public/locales/en/products.json | 2 +- public/locales/zh/common.json | 2 ++ src/components/SearchForm.jsx | 4 ++-- src/components/SecondHeaderWrapper.jsx | 28 ++++++++++++++++++++++ src/hooks/useProductsSets.js | 16 ++++++------- src/main.jsx | 4 ++++ src/stores/Products/Index.js | 13 ++++++++++- src/views/products/Audit.jsx | 32 ++++++++++++++++++++++++++ src/views/products/Detail.jsx | 10 ++++++++ src/views/products/Index.jsx | 26 ++++++++++++++------- 11 files changed, 119 insertions(+), 20 deletions(-) create mode 100644 src/components/SecondHeaderWrapper.jsx create mode 100644 src/views/products/Audit.jsx create mode 100644 src/views/products/Detail.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 4575ad3..0810454 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -11,6 +11,7 @@ "Close": "Close", "Save": "Save", "Edit": "Edit", + "Audit": "Audit", "Delete": "Delete", "Add": "Add", "View": "View", @@ -19,6 +20,7 @@ "Upload": "Upload", "preview": "Preview", "Total": "Total", + "Action": "Action", "Login": "Login", "Username": "Username", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 4813a31..2076875 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -21,6 +21,6 @@ "AuState": "Audit State", "CreatedBy": "Created By", "CreateDate": "Create Date", - "Auditors": "Auditors", + "AuditedBy": "Audited By", "AuditDate": "Audit Date" } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index d689719..4ef0694 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -11,6 +11,7 @@ "Close": "关闭", "Save": "保存", "Edit": "编辑", + "Audit": "审核", "Delete": "删除", "Add": "添加", "View": "查看", @@ -19,6 +20,7 @@ "Upload": "上传", "preview": "预览", "Total": "总数", + "Action": "操作", "Login": "登录", "Username": "账号", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 6ccbeae..97ca443 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -47,7 +47,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { { key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') }, ], 'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' }, - 'auditStatus': { key: 'auditStatus', transform: (value) => value?.value || value?.key || '', default: '' }, + 'auditState': { key: 'auditState', transform: (value) => value?.value || value?.key || '', default: '' }, 'agency': { key: 'agency', transform: (value) => { @@ -225,7 +225,7 @@ function getFields(props) { { value: '0', label: 'New' }, { value: '1', label: 'Pending' }, { value: '2', label: 'Approve' }, - { value: '3', label: 'Rejected' }, + // { value: '3', label: 'Rejected' }, { value: '4', label: 'Published' }, ]} /> diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx new file mode 100644 index 0000000..2b2ffca --- /dev/null +++ b/src/components/SecondHeaderWrapper.jsx @@ -0,0 +1,28 @@ +import { Outlet, useNavigate } from 'react-router-dom'; +import { Layout, Flex, theme } from 'antd'; +import BackBtn from './BackBtn'; + +const { Content, Header } = Layout; +const HeaderWrapper = ({ children, header, ...props }) => { + const navigate = useNavigate(); + const { + token: { colorBgContainer }, + } = theme.useToken(); + return ( + <> + +
+ + {/* {header} */} +
{header}
+ +
+
+ + {children || } + +
+ + ); +}; +export default HeaderWrapper; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 0834d52..4545d91 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -11,14 +11,14 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ - { label: t('products:type.Experience'), value: t('products:type.Experience') }, - {label: t("products:type.Car"), value: t("products:type.Car"),}, - {label: t("products:type.Guide"), value: t("products:type.Guide"),}, - {label: t("products:type.Package"), value: t("products:type.Package"),}, - {label: t("products:type.Attractions"), value: t("products:type.Attractions"),}, - {label: t("products:type.Meals"), value: t("products:type.Meals"),}, - {label: t("products:type.Extras"), value: t("products:type.Extras"),}, - {label: t("products:type.Special"), value: t("products:type.Special")}, + { label: t('products:type.Experience'), value: 'Experience', key: 'Experience' }, + { label: t('products:type.Car'), value: 'Car', key: 'Car' }, + { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, + { label: t('products:type.Package'), value: 'Package', key: 'Package' }, + { label: t('products:type.Attractions'), value: 'Attractions', key: 'Attractions' }, + { label: t('products:type.Meals'), value: 'Meals', key: 'Meals' }, + { label: t('products:type.Extras'), value: 'Extras', key: 'Extras' }, + { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); }, [i18n.language]); diff --git a/src/main.jsx b/src/main.jsx index 0f7c4fc..3733462 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -31,6 +31,8 @@ import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; import ProductsIndex from '@/views/products/Index'; +import ProductsDetail from '@/views/products/Detail'; +import ProductsAudit from '@/views/products/Audit'; import './i18n'; @@ -67,6 +69,8 @@ const router = createBrowserRouter([ { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, { path: "products",element:}, + { path: "products/:travel_agency_id/audit",element:}, + { path: "products/:travel_agency_id",element:}, ] }, { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 87066a9..872bec9 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -6,7 +6,18 @@ import { HT_HOST } from '@/config'; const initialState = { loading: false, - productsList: [], + productsList: [ + { + 'audit_date': '2001-03-03', + 'travel_agency_name': '新油低外', + 'travel_agency_id': '650000200301029585', + 'created_by': '冯丽', + 'create_date': '1989-06-20', + 'lastedit_memo': 'nostrud ad eu', + 'audited_by': '黎静', + 'audit_state': '1', + }, + ], }; export const useProductsStore = create( devtools((set, get) => ({ diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx new file mode 100644 index 0000000..f4187a2 --- /dev/null +++ b/src/views/products/Audit.jsx @@ -0,0 +1,32 @@ +import { createContext, useContext, useEffect, useState } from 'react'; +import { Button, Table, Tabs } from 'antd'; +import { useProductsTypes } from '@/hooks/useProductsSets'; +import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; + +const Header = () => { + return ( +
+
+ + +
+ ); +}; + +const TypesTabs = () => { + const productsTypes = useProductsTypes(); + return ( + + ) +} + +const Audit = ({ ...props }) => { + return ( + <> + }> + + + + ); +}; +export default Audit; diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx new file mode 100644 index 0000000..807e0e9 --- /dev/null +++ b/src/views/products/Detail.jsx @@ -0,0 +1,10 @@ +import { createContext, useContext, useEffect, useState } from 'react'; +import { Table } from 'antd'; + +const Detail = ((props) => { + return ( + <> + + ); +}); +export default Detail; diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index e75ac50..b793b2f 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; import { Row, Col, Space, Typography, Table, Button } from 'antd'; import useProductsStore from '@/stores/Products/Index'; import { usingStorage } from '@/hooks/usingStorage'; @@ -10,20 +11,29 @@ import { useProductsTypes } from '@/hooks/useProductsSets'; function Index() { const { t } = useTranslation(); const { userId } = usingStorage(); - const [loading, productsList] = useProductsStore((state) => state.productsList); + const [loading, productsList] = useProductsStore((state) => [state.loading, state.productsList]); const [noticeList, setNoticeList] = useState([]); useEffect(() => {}, []); const showTotal = (total) => `Total ${total} items`; const columns = [ - { title: t('products:Vendor'), key: '', dataIndex: '' }, - { title: t('products:AuState'), key: '', dataIndex: '' }, - { title: t('products:CreatedBy'), key: '', dataIndex: '' }, - { title: t('products:CreateDate'), key: '', dataIndex: '' }, - { title: t('products:Auditors'), key: '', dataIndex: '' }, - { title: t('products:AuditDate'), key: '', dataIndex: '' }, - { title: '', key: 'action', render: () => }, + { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, + { title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by' }, + { title: t('products:CreateDate'), key: 'create_date', dataIndex: 'create_date' }, + { title: t('products:AuState'), key: 'audit_state', dataIndex: 'audit_state' }, + { title: t('products:AuditedBy'), key: 'audited_by', dataIndex: 'audited_by' }, + { title: t('products:AuditDate'), key: 'audit_date', dataIndex: 'audit_date' }, + { + title: '', + key: 'action', + render: (_, r) => ( + + {t('Edit')} + {t('Audit')} + + ), + }, ]; return ( From f323f0b511e858f253afec08ab85973e62a16f59 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 18 Jun 2024 13:57:55 +0800 Subject: [PATCH 03/46] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E7=8A=B6=E6=80=81?= 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 | 18 +++++++++++++++++- public/locales/zh/common.json | 3 +++ public/locales/zh/products.json | 20 ++++++++++++++++++-- src/components/AuditStateSelector.jsx | 12 ++++++++++++ src/components/SearchForm.jsx | 20 ++++++-------------- src/components/SecondHeaderWrapper.jsx | 2 +- src/hooks/useProductsSets.js | 12 ++++++------ src/views/products/Index.jsx | 10 ++++++---- 9 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 src/components/AuditStateSelector.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 0810454..09471cf 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -21,6 +21,9 @@ "preview": "Preview", "Total": "Total", "Action": "Action", + "Import": "Import", + "Export": "Export", + "Copy": "Copy", "Login": "Login", "Username": "Username", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 2076875..637b8f8 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -16,11 +16,27 @@ "Rejected": "Rejected", "Published": "Published" }, + "Status": "Status", + "Title": "Title", "Vendor": "Vendor", "AuState": "Audit State", "CreatedBy": "Created By", "CreateDate": "Create Date", "AuditedBy": "Audited By", - "AuditDate": "Audit Date" + "AuditDate": "Audit Date", + + "Quotation": "Quotation", + "Unit": "Unit", + "GroupSize": "Group Size", + "UseDates": "Use Dates", + "Weekdays": "Weekdays", + + "AgeType": { + "Type": "Age Type", + "Adult": "Adult", + "Child": "Child" + }, + + "#": "#" } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 4ef0694..2c54705 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -21,6 +21,9 @@ "preview": "预览", "Total": "总数", "Action": "操作", + "Import": "导入", + "Export": "导出", + "Copy": "复制", "Login": "登录", "Username": "账号", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 326bd78..8d5c4e9 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -16,11 +16,27 @@ "Rejected": "已拒绝", "Published": "已发布" }, + "Status": "状态", + "Title": "名称", "Vendor": "供应商", "AuState": "审核状态", "CreatedBy": "提交人员", "CreateDate": "提交时间", - "Auditors": "审核人员", - "AuditDate": "审核时间" + "AuditedBy": "审核人员", + "AuditDate": "审核时间", + + "Quotation": "报价", + "Unit": "单位", + "GroupSize": "人等", + "UseDates": "使用日期", + "Weekdays": "有效日", + + "AgeType": { + "Type": "人群", + "Adult": "成人", + "Child": "儿童" + }, + + "#": "#" } diff --git a/src/components/AuditStateSelector.jsx b/src/components/AuditStateSelector.jsx new file mode 100644 index 0000000..db4c77d --- /dev/null +++ b/src/components/AuditStateSelector.jsx @@ -0,0 +1,12 @@ +import { Select } from 'antd'; +import { useProductsAuditStates } from '@/hooks/useProductsSets'; + +const AuditStateSelector = ({ ...props }) => { + const states = useProductsAuditStates(); + return ( + <> + + + , fieldProps?.auditState?.col || 3 ), diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index 2b2ffca..097b0d4 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -14,7 +14,7 @@ const HeaderWrapper = ({ children, header, ...props }) => {
{/* {header} */} -
{header}
+
{header}
diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 4545d91..ee33d47 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -27,17 +27,17 @@ export const useProductsTypes = () => { }; -export const useProductsAuditStatus = () => { +export const useProductsAuditStates = () => { const [types, setTypes] = useState([]); const { t, i18n } = useTranslation(); useEffect(() => { const newData = [ - { value: '0', label: 'New' }, - { value: '1', label: 'Pending' }, - { value: '2', label: 'Approve' }, - { value: '3', label: 'Rejected' }, - { value: '4', label: 'Published' }, + { value: '0', label: t('products:auditState.New') }, + { value: '1', label: t('products:auditState.Pending') }, + { value: '2', label: t('products:auditState.Approve') }, + { value: '3', label: t('products:auditState.Rejected') }, + { value: '4', label: t('products:auditState.Published') }, ]; setTypes(newData); }, [i18n.language]); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index b793b2f..df1b189 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -17,6 +17,7 @@ function Index() { useEffect(() => {}, []); const showTotal = (total) => `Total ${total} items`; + const columns = [ { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, { title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by' }, @@ -38,9 +39,6 @@ function Index() { return ( { }} />
-
+
From 232a8f85c875a1d1b0f0fd8b4f86b58dbcce668d Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 10:19:43 +0800 Subject: [PATCH 04/46] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2:=20=E8=A1=A8=E6=A0=BC,=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/products.json | 2 +- src/hooks/useProductsSets.js | 10 +- src/stores/Products/Index.js | 246 +++++++++++++++++++++++++++++++- src/views/App.jsx | 1 - src/views/products/Audit.jsx | 106 ++++++++++++-- src/views/products/Index.jsx | 6 +- 6 files changed, 351 insertions(+), 20 deletions(-) diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 8d5c4e9..932fc24 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -30,7 +30,7 @@ "Unit": "单位", "GroupSize": "人等", "UseDates": "使用日期", - "Weekdays": "有效日", + "Weekdays": "有效日/周X", "AgeType": { "Type": "人群", diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index ee33d47..888368d 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -33,11 +33,11 @@ export const useProductsAuditStates = () => { useEffect(() => { const newData = [ - { value: '0', label: t('products:auditState.New') }, - { value: '1', label: t('products:auditState.Pending') }, - { value: '2', label: t('products:auditState.Approve') }, - { value: '3', label: t('products:auditState.Rejected') }, - { value: '4', label: t('products:auditState.Published') }, + { key: '0', value: '0', label: t('products:auditState.New') }, + { key: '1', value: '1', label: t('products:auditState.Pending') }, + { key: '2', value: '2', label: t('products:auditState.Approve') }, + { key: '3', value: '3', label: t('products:auditState.Rejected') }, + { key: '4', value: '4', label: t('products:auditState.Published') }, ]; setTypes(newData); }, [i18n.language]); diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 872bec9..1747757 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -3,10 +3,19 @@ import { devtools } from 'zustand/middleware'; import { fetchJSON } from '@/utils/request'; import { HT_HOST } from '@/config'; +import { groupBy } from '@/utils/commons'; + +const searchAgency = async (param) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); + +}; +const getAgencyProducts = async (param) => { + const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); +}; const initialState = { loading: false, - productsList: [ + agencyList: [ { 'audit_date': '2001-03-03', 'travel_agency_name': '新油低外', @@ -18,6 +27,237 @@ const initialState = { 'audit_state': '1', }, ], + activeAgency: {}, + agencyProducts: groupBy([ + { + "info": { + "id": "640000198509289851", + "title": "如拉下完公", + "code": "grlkt", + "type": "Guide", + "audit_state": "1", + "create_date": "2022-01-13", + "created_by": "郝涛", + "travel_agency_id": "710000200712195349", + "travel_agency_name": "国得气验", + "lastedit_memo": "划百引程级门会需代领主属快。", + "remarks": "及决对金利低集小理电和按常如门。", + "duration": 2, + "duration_unit": "m", + "open_weekdays": "6", + "recommends_rate": 3, + "dept": 1, + "display_to_c": "2", + "km": 27, + "city_id": 77, + "city_name": "称命" + }, + "quotation": [ + { + "id": "21000020030611324X", + "value": 70, + "currency": "CNY", + "unit": "团", + "age_type": "儿童", + "group_size_min": 4, + "group_size_max": 4, + "use_dates_start": "2004-01-19", + "use_dates_end": "1990-03-10", + "weekdays": "4", + "audit_state": "ea pariatur", + "lastedit_memo": "sunt" + }, + { + "id": "610000197306240177", + "value": 86, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 6, + "group_size_max": 8, + "use_dates_start": "1996-12-16", + "use_dates_end": "1974-11-19", + "weekdays": "4", + "audit_state": "aliqua aute quis ipsum", + "lastedit_memo": "commodo adipisicing ea ipsum" + } + ], + "lgc_details": [ + { + "lgc": "mollit", + "title": "林运但", + "description": "学克信图走法因心委周说步将且文手越。", + "id": "35" + }, + { + "lgc": "et laborum", + "title": "备上引深量知量", + "description": "到听少文话包由北层中争二调原务越明在。", + "id": "74" + }, + { + "lgc": "minim velit", + "title": "安都始新", + "description": "取影压前手府要青白支大而。", + "id": "23" + } + ] + }, + { + "info": { + "id": "41000019901227754X", + "title": "据划京少国取", + "code": "ore", + "type": "Guide", + "audit_state": "2", + "create_date": "1979-01-31", + "created_by": "陆芳", + "travel_agency_id": "110000198612200137", + "travel_agency_name": "少平酸型", + "lastedit_memo": "八想军也装运知长示各院步济水。", + "remarks": "千改原统实专回列参目党却是样与后收。", + "duration": 3, + "duration_unit": "d", + "open_weekdays": "5", + "recommends_rate": 5, + "dept": 1, + "display_to_c": "2", + "km": 30, + "city_id": 62, + "city_name": "业入" + }, + "quotation": [ + { + "id": "37000019760525515X", + "value": 93, + "currency": "CNY", + "unit": "团", + "age_type": "成人", + "group_size_min": 7, + "group_size_max": 11, + "use_dates_start": "1992-11-22", + "use_dates_end": "1997-07-16", + "weekdays": "7", + "audit_state": "id nulla irure cupidatat", + "lastedit_memo": "quis aute reprehenderit consectetur" + }, + { + "id": "150000199506023175", + "value": 90, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 9, + "group_size_max": 10, + "use_dates_start": "2007-09-11", + "use_dates_end": "2013-07-27", + "weekdays": "5", + "audit_state": "commodo ad ut", + "lastedit_memo": "id anim incididunt" + } + ], + "lgc_details": [ + { + "lgc": "adipisicing elit Excepteur in", + "title": "很很结龙认", + "description": "事起复京长立然将采共层列工。", + "id": "43" + }, + { + "lgc": "dolore fugiat", + "title": "专中小", + "description": "示史想当集认点离反而原化精满并计前。", + "id": "28" + }, + { + "lgc": "sunt consectetur ea cillum", + "title": "他率带没", + "description": "节经厂面际是统表王活基书色活至是干验。", + "id": "83" + }, + { + "lgc": "incididunt labore fugiat", + "title": "精话西改", + "description": "须事金性别民学少拉个且须专需断连。", + "id": "97" + }, + { + "lgc": "dolore id", + "title": "文技话", + "description": "上任成条到则查支外很素给务府三。", + "id": "99" + } + ] + }, + { + "info": { + "id": "44000019990112280X", + "title": "节到和", + "code": "ixlmndtmz", + "type": "Meals", + "audit_state": "1", + "create_date": "2006-12-30", + "created_by": "易敏", + "travel_agency_id": "640000197111288408", + "travel_agency_name": "术备带走", + "lastedit_memo": "认队什教调问传改万消然声地全。", + "remarks": "属须厂几问总识看部群该克员方。", + "duration": 2, + "duration_unit": "m", + "open_weekdays": "6", + "recommends_rate": 3, + "dept": 2, + "display_to_c": "1", + "km": 13, + "city_id": 55, + "city_name": "铁以" + }, + "quotation": [ + { + "id": "13000019860219219X", + "value": 88, + "currency": "CNY", + "unit": "团", + "age_type": "儿童", + "group_size_min": 2, + "group_size_max": 4, + "use_dates_start": "1991-03-19", + "use_dates_end": "1974-03-13", + "weekdays": "3", + "audit_state": "officia voluptate ad adipisicing dolore", + "lastedit_memo": "Duis amet veniam enim" + }, + { + "id": "420000201706118123", + "value": 61, + "currency": "CNY", + "unit": "人", + "age_type": "儿童", + "group_size_min": 4, + "group_size_max": 10, + "use_dates_start": "1992-04-23", + "use_dates_end": "1970-07-19", + "weekdays": "5", + "audit_state": "commodo labore", + "lastedit_memo": "ullamco anim culpa do in" + } + ], + "lgc_details": [ + { + "lgc": "ut minim", + "title": "回等这意", + "description": "农满界个整千书得被写况空派会想头无。", + "id": "40" + }, + { + "lgc": "laborum id elit irure commodo", + "title": "增正数白养土子", + "description": "么划才共别程以元于族完难变。", + "id": "84" + } + ] + } +], row => row.info.type), }; export const useProductsStore = create( devtools((set, get) => ({ @@ -25,7 +265,9 @@ export const useProductsStore = create( ...initialState, // state actions - setProductsList: (productsList) => set({ productsList }), + setAgencyList: (agencyList) => set({ agencyList }), + setActiveAgency: activeAgency => set({ activeAgency }), + setAgencyProducts: agencyProducts => set({ agencyProducts }), reset: () => set(initialState), diff --git a/src/views/App.jsx b/src/views/App.jsx index cbb2949..0edd221 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -152,7 +152,6 @@ function App() { { label: {t('account:management.tile')}, key: '3' }, { type: 'divider' }, { label: {t('Logout')}, key: '4' }, - { label: {t('ChangeVendor')}, key: 'change-vendor' }, ], { type: 'divider' }, { label: <>v{BUILD_VERSION}, key: 'BUILD_VERSION' }, diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index f4187a2..cc5b79f 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,14 +1,22 @@ import { createContext, useContext, useEffect, useState } from 'react'; -import { Button, Table, Tabs } from 'antd'; +import { Link, useLocation, } from 'react-router-dom'; +import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; import { useProductsTypes } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; +import { useTranslation } from 'react-i18next'; +import useProductsStore from '@/stores/Products/Index'; +import { isEmpty } from '@/utils/commons'; -const Header = () => { +const Header = ({title, ...props}) => { + const { t } = useTranslation(); return ( -
-
- - +
+
+

{title}

+
+ {/* */} + {/* */} +
); }; @@ -20,11 +28,93 @@ const TypesTabs = () => { ) } +const subjectComponents = { + // 'sum_profit': ProfitTable, + // 'in_order_count': Count, + // 'confirm_order_count': Count, + // 'depart_order_count': Count, + // 'confirm_rates': Rates, + // 'praise_rates': Rates, + // 'sum_person_num': Count, +}; + +const PriceTable = ({dataSource}) => { + const { t } = useTranslation('products'); + // console.log(dataSource, ); + const columns = [ + { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, + { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, + // {key: 'price', title: t('Currency'), }, + // {key: 'currency', title: t('Currency'), }, + // {key: 'unit', title: t('Unit'), }, + { key: 'ageType', dataIndex: ['age_type'], title: t('AgeType.Type') }, + { + key: 'groupSize', + dataIndex: ['group_size_min'], + title: t('GroupSize'), + render: (_, { group_size_min, group_size_max} ) => `${group_size_min} - ${group_size_max}`, + }, + { + key: 'useDates', + dataIndex: ['use_dates_start'], + title: t('UseDates'), + render: (_, { use_dates_start, use_dates_end} ) => `${use_dates_start} ~ ${use_dates_end}`, + }, + { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays'), }, + {key: 'status', title: t('Status'), render: () => '已通过'}, + { + title: '', + key: 'action', + render: () => ( + + + + + ), + }, + ]; + return
r.id} />; +} + +/** + * + */ +const TypesPanels = () => { + const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + // console.log(agencyProducts); + const productsTypes = useProductsTypes(); + const [activeKey, setActiveKey] = useState([]); + const [showTypes, setShowTypes] = useState([]); + useEffect(() => { + // 只显示有产品的类型 + const hasDataTypes = Object.keys(agencyProducts); + const _show = productsTypes + .filter((kk) => hasDataTypes.includes(kk.value)) + .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + setShowTypes(_show); + + setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); + + return () => {}; + }, [productsTypes, agencyProducts]); + + const onCollapseChange = (_activeKey) => { + setActiveKey(_activeKey) + } + return ( + + ) +} + const Audit = ({ ...props }) => { + // const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + return ( <> - }> - + }> +
+ + {/* */}
); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index df1b189..0562519 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -1,17 +1,17 @@ import { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { Row, Col, Space, Typography, Table, Button } from 'antd'; -import useProductsStore from '@/stores/Products/Index'; import { usingStorage } from '@/hooks/usingStorage'; import SearchForm from '@/components/SearchForm'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; +import useProductsStore from '@/stores/Products/Index'; import { useProductsTypes } from '@/hooks/useProductsSets'; function Index() { const { t } = useTranslation(); const { userId } = usingStorage(); - const [loading, productsList] = useProductsStore((state) => [state.loading, state.productsList]); + const [loading, agencyList, ] = useProductsStore((state) => [state.loading, state.agencyList, ]); const [noticeList, setNoticeList] = useState([]); useEffect(() => {}, []); @@ -56,7 +56,7 @@ function Index() { />
-
+
From a579ee4ee5f3686e287408296da17c8d59c39725 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 10:20:21 +0800 Subject: [PATCH 05/46] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=8F=82=E6=95=B0:=20l?= =?UTF-8?q?gc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/LanguageSwitcher.jsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/i18n/LanguageSwitcher.jsx b/src/i18n/LanguageSwitcher.jsx index ac1f009..af87005 100644 --- a/src/i18n/LanguageSwitcher.jsx +++ b/src/i18n/LanguageSwitcher.jsx @@ -1,6 +1,12 @@ -import React, { useState } from 'react'; -import { Dropdown, Menu } from 'antd'; +import { useState, useEffect } from 'react'; +import { Dropdown } from 'antd'; import { useTranslation } from 'react-i18next'; +import { appendRequestParams } from '@/utils/request'; + +const i18n_to_htcode = { + 'zh': 2, + 'en': 1, +}; /** * 语言选择组件 @@ -8,6 +14,13 @@ import { useTranslation } from 'react-i18next'; const Language = () => { const { t, i18n } = useTranslation(); const [selectedKeys, setSelectedKeys] = useState([i18n.language]); + + useEffect(() => { + appendRequestParams('lgc', i18n_to_htcode[i18n.language]); + + return () => {}; + }, [i18n.language]); + // 切换语言事件 const handleChangeLanguage = ({ key }) => { setSelectedKeys([key]); From d24c34d4acf93dacae1fa35e427a37ade7cda7d3 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 13:45:11 +0800 Subject: [PATCH 06/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E9=A6=96=E9=A1=B5?= 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 | 4 ++- src/components/SearchForm.jsx | 8 +++--- src/hooks/useProductsSets.js | 16 ++++++++---- src/stores/Products/Index.js | 12 +++++++-- src/views/products/Audit.jsx | 44 +++++++++++++-------------------- src/views/products/Index.jsx | 35 +++++++++++++++++--------- 9 files changed, 80 insertions(+), 52 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 09471cf..b113807 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -25,6 +25,10 @@ "Export": "Export", "Copy": "Copy", + "Table": { + "Total": "Total {{total}} items" + }, + "Login": "Login", "Username": "Username", "Password": "Password", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 637b8f8..35c6d67 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -12,11 +12,12 @@ "auditState": { "New": "New", "Pending": "Pending", - "Approve": "Approve", + "Approved": "Approved", "Rejected": "Rejected", "Published": "Published" }, "Status": "Status", + "State": "State", "Title": "Title", "Vendor": "Vendor", @@ -27,6 +28,8 @@ "AuditDate": "Audit Date", "Quotation": "Quotation", + "Offer": "Offer", + "Unit": "Unit", "GroupSize": "Group Size", "UseDates": "Use Dates", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 2c54705..e3239a1 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -25,6 +25,10 @@ "Export": "导出", "Copy": "复制", + "Table": { + "Total": "共 {{total}} 条" + }, + "Login": "登录", "Username": "账号", "Password": "密码", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 932fc24..d4d640d 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -12,11 +12,12 @@ "auditState": { "New": "新增", "Pending": "待审核", - "Approve": "已通过", + "Approved": "已通过", "Rejected": "已拒绝", "Published": "已发布" }, "Status": "状态", + "State": "状态", "Title": "名称", "Vendor": "供应商", @@ -27,6 +28,7 @@ "AuditDate": "审核时间", "Quotation": "报价", + "Offer": "报价", "Unit": "单位", "GroupSize": "人等", "UseDates": "使用日期", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index ab5626b..9f55352 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -50,7 +50,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { { key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') }, ], 'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' }, - 'auditState': { key: 'auditState', transform: (value) => value?.value || value?.key || '', default: '' }, + 'audit_state': { key: 'audit_state', transform: (value) => value?.value || value?.key || '', default: '' }, 'agency': { key: 'agency', transform: (value) => { @@ -217,12 +217,12 @@ function getFields(props) { fieldProps?.agency?.col || 6 ), item( - 'auditState', + 'audit_state', 99, - + , - fieldProps?.auditState?.col || 3 + fieldProps?.audit_state?.col || 3 ), ]; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 888368d..f002991 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -33,14 +33,20 @@ export const useProductsAuditStates = () => { useEffect(() => { const newData = [ - { key: '0', value: '0', label: t('products:auditState.New') }, - { key: '1', value: '1', label: t('products:auditState.Pending') }, - { key: '2', value: '2', label: t('products:auditState.Approve') }, - { key: '3', value: '3', label: t('products:auditState.Rejected') }, - { key: '4', value: '4', label: t('products:auditState.Published') }, + { 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') }, ]; setTypes(newData); }, [i18n.language]); return types; }; + +export const useProductsAuditStatesMapVal = (value) => { + const stateSets = useProductsAuditStates(); + const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); + return stateMapVal; +}; diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 1747757..ecd1fe1 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -5,9 +5,9 @@ import { fetchJSON } from '@/utils/request'; import { HT_HOST } from '@/config'; import { groupBy } from '@/utils/commons'; -const searchAgency = async (param) => { +const searchAgencyAction = async (param) => { const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); - + return errcode !== 0 ? [] : result; }; const getAgencyProducts = async (param) => { const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); @@ -265,6 +265,7 @@ export const useProductsStore = create( ...initialState, // state actions + setLoading: loading => set({ loading }), setAgencyList: (agencyList) => set({ agencyList }), setActiveAgency: activeAgency => set({ activeAgency }), setAgencyProducts: agencyProducts => set({ agencyProducts }), @@ -272,6 +273,13 @@ export const useProductsStore = create( reset: () => set(initialState), // side effects + searchAgency: async (param) => { + const { setLoading, setAgencyList } = get(); + setLoading(true); + const res = await searchAgencyAction(param); + setAgencyList(res); + setLoading(false); + }, })) ); export default useProductsStore; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index cc5b79f..cbc8fdb 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,7 +1,7 @@ import { createContext, useContext, useEffect, useState } from 'react'; import { Link, useLocation, } from 'react-router-dom'; import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; -import { useProductsTypes } from '@/hooks/useProductsSets'; +import { useProductsTypes, useProductsAuditStatesMapVal, useProductsAuditStates } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; import useProductsStore from '@/stores/Products/Index'; @@ -21,26 +21,10 @@ const Header = ({title, ...props}) => { ); }; -const TypesTabs = () => { - const productsTypes = useProductsTypes(); - return ( - - ) -} - -const subjectComponents = { - // 'sum_profit': ProfitTable, - // 'in_order_count': Count, - // 'confirm_order_count': Count, - // 'depart_order_count': Count, - // 'confirm_rates': Rates, - // 'praise_rates': Rates, - // 'sum_person_num': Count, -}; - -const PriceTable = ({dataSource}) => { +const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); - // console.log(dataSource, ); + const stateMapVal = useProductsAuditStatesMapVal(); + const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, @@ -52,23 +36,29 @@ const PriceTable = ({dataSource}) => { key: 'groupSize', dataIndex: ['group_size_min'], title: t('GroupSize'), - render: (_, { group_size_min, group_size_max} ) => `${group_size_min} - ${group_size_max}`, + render: (_, { group_size_min, group_size_max }) => `${group_size_min} - ${group_size_max}`, }, { 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 }) => `${use_dates_start} ~ ${use_dates_end}`, + }, + { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays') }, + { + key: 'state', + title: t('State'), + render: (_, r) => { + return stateMapVal[`${r.info.audit_state}`]?.label; + }, }, - { key: 'weekdays', dataIndex: ['weekdays'], title: t('Weekdays'), }, - {key: 'status', title: t('Status'), render: () => '已通过'}, { title: '', key: 'action', render: () => ( - - + + ), }, @@ -90,7 +80,7 @@ const TypesPanels = () => { const hasDataTypes = Object.keys(agencyProducts); const _show = productsTypes .filter((kk) => hasDataTypes.includes(kk.value)) - .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); setShowTypes(_show); setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 0562519..02d57ec 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -1,22 +1,29 @@ -import { useEffect, useState } from 'react'; +import { useEffect } from 'react'; import { Link } from 'react-router-dom'; -import { Row, Col, Space, Typography, Table, Button } from 'antd'; -import { usingStorage } from '@/hooks/usingStorage'; +import { Row, Col, Space, Table } from 'antd'; import SearchForm from '@/components/SearchForm'; import dayjs from 'dayjs'; import { useTranslation } from 'react-i18next'; import useProductsStore from '@/stores/Products/Index'; -import { useProductsTypes } from '@/hooks/useProductsSets'; +import useFormStore from '@/stores/Form'; +import { objectMapper } from '@/utils/commons'; function Index() { const { t } = useTranslation(); - const { userId } = usingStorage(); - const [loading, agencyList, ] = useProductsStore((state) => [state.loading, state.agencyList, ]); + const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]); + const formValuesToSub = useFormStore(state => state.formValuesToSub); - const [noticeList, setNoticeList] = useState([]); - useEffect(() => {}, []); + const handleSearchAgency = (formVal = undefined) => { + const { starttime, endtime, ...param } = formVal || formValuesToSub; + const searchParam = objectMapper(param, { agency: 'travel_agency_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + searchAgency(searchParam); + } - const showTotal = (total) => `Total ${total} items`; + useEffect(() => { + handleSearchAgency(); + }, []); + + const showTotal = (total) => t('Table.Total', { total }); const columns = [ { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, @@ -40,18 +47,22 @@ function Index() { { + handleSearchAgency(formVal); }} /> From f8ffaf899d77dd7d106a31a8328da09e3dcf82e3 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 19 Jun 2024 15:55:06 +0800 Subject: [PATCH 07/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E5=AE=A1=E6=A0=B8=E4=BA=A7?= =?UTF-8?q?=E5=93=81,=20=E4=BB=B7=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 7 +++ public/locales/zh/products.json | 7 +++ src/stores/Products/Index.js | 64 ++++++++++++++++++------ src/views/products/Audit.jsx | 87 +++++++++++++++++++++++++++------ 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 35c6d67..6033e89 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -16,6 +16,13 @@ "Rejected": "Rejected", "Published": "Published" }, + "auditStateAction": { + "New": "New", + "Pending": "Pending", + "Approved": "Approve", + "Rejected": "Rejecte", + "Published": "Publish" + }, "Status": "Status", "State": "State", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index d4d640d..01f7836 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -16,6 +16,13 @@ "Rejected": "已拒绝", "Published": "已发布" }, + "auditStateAction": { + "New": "新增", + "Pending": "待审核", + "Approved": "审核通过", + "Rejected": "审核拒绝", + "Published": "发布上线" + }, "Status": "状态", "State": "状态", diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index ecd1fe1..544c478 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -1,32 +1,53 @@ import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; -import { fetchJSON } from '@/utils/request'; +import { fetchJSON, postForm } from '@/utils/request'; import { HT_HOST } from '@/config'; import { groupBy } from '@/utils/commons'; -const searchAgencyAction = async (param) => { +export const searchAgencyAction = async (param) => { const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); return errcode !== 0 ? [] : result; }; -const getAgencyProducts = async (param) => { - const { errcode, Result, Result1 } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/products_search`, param); +export const getAgencyProductsAction = async (param) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products`, param); + return errcode !== 0 ? [] : result; +}; + +export const postProductsQuoteAudit = async (auditState, quoteRow) => { + const postbody = { + audit_state: auditState, + id: quoteRow.id, + travel_agency_id: quoteRow.info.travel_agency_id, + }; + const formData = new FormData(); + Object.keys(postbody).forEach((key) => { + formData.append(key, postbody[key]); + }); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-quote-audit`, formData); + return json; + // return errcode !== 0 ? {} : result; +}; + +export const postProductsAudit = async (auditState, infoRow) => { + const postbody = { + audit_state: auditState, + id: infoRow.id, + travel_agency_id: infoRow.travel_agency_id, + }; + const formData = new FormData(); + Object.keys(postbody).forEach((key) => { + formData.append(key, postbody[key]); + }); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-audit`, formData); + return json; + // const { errcode, result } = json; + // return errcode !== 0 ? {} : result; }; const initialState = { loading: false, - agencyList: [ - { - 'audit_date': '2001-03-03', - 'travel_agency_name': '新油低外', - 'travel_agency_id': '650000200301029585', - 'created_by': '冯丽', - 'create_date': '1989-06-20', - 'lastedit_memo': 'nostrud ad eu', - 'audited_by': '黎静', - 'audit_state': '1', - }, - ], + agencyList: [], activeAgency: {}, agencyProducts: groupBy([ { @@ -280,6 +301,17 @@ export const useProductsStore = create( setAgencyList(res); setLoading(false); }, + + getAgencyProducts: async (param) => { + const { setLoading, setActiveAgency, setAgencyProducts } = get(); + setLoading(true); + const res = await getAgencyProductsAction(param); + const productsData = groupBy(res, row => row.info.type); + setAgencyProducts(productsData); + setActiveAgency(res[0].info); + setLoading(false); + }, + })) ); export default useProductsStore; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index cbc8fdb..3a4f3e6 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,14 +1,31 @@ -import { createContext, useContext, useEffect, useState } from 'react'; -import { Link, useLocation, } from 'react-router-dom'; -import { Button, Collapse, Table, Tabs, Typography, Space, } from 'antd'; -import { useProductsTypes, useProductsAuditStatesMapVal, useProductsAuditStates } from '@/hooks/useProductsSets'; +import { useEffect, useState } from 'react'; +import { useParams, } from 'react-router-dom'; +import { App, Button, Collapse, Table, Space, } from 'antd'; +import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; -import useProductsStore from '@/stores/Products/Index'; +import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; -const Header = ({title, ...props}) => { +const Header = ({ title, agency, ...props}) => { const { t } = useTranslation(); + const { message, notification } = App.useApp(); + const handleAuditItem = (state, row) => { + postProductsQuoteAudit(state, row) + .then((json) => { + if (json.errcode === 0) { + message.success('✔'); + } + }) + .catch((ex) => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }); + }); + }; return (
@@ -16,15 +33,37 @@ const Header = ({title, ...props}) => {
{/* */} {/* */} - + + {/* todo: export */} +
); }; const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); + const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); + const handleAuditPriceItem = (state, row) => { + postProductsQuoteAudit(state, row) + .then((json) => { + if (json.errcode === 0) { + message.success('✔'); + } + }) + .catch((ex) => { + notification.error({ + message: 'Notification', + description: ex.message, + placement: 'top', + duration: 4, + }); + }); + }; + const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, @@ -55,10 +94,10 @@ const PriceTable = ({dataSource, loading}) => { { title: '', key: 'action', - render: () => ( + render: (_, r) => ( - - + + ), }, @@ -76,11 +115,19 @@ const TypesPanels = () => { const [activeKey, setActiveKey] = useState([]); const [showTypes, setShowTypes] = useState([]); useEffect(() => { - // 只显示有产品的类型 + // 只显示有产品的类型; 展开产品的价格表, 合并名称列 const hasDataTypes = Object.keys(agencyProducts); const _show = productsTypes .filter((kk) => hasDataTypes.includes(kk.value)) - .map((ele) => ({ ...ele, children: r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i=== 0 ? c.quotation.length : 0 }))), [])} /> })); + .map((ele) => ({ + ...ele, + children: ( + r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} + /> + ), + })); setShowTypes(_show); setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); @@ -97,14 +144,24 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - // const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); + const { agencyId: travel_agency_id } = useParams(); + const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); + + const handleGetAgencyProducts = () => { + getAgencyProducts({ travel_agency_id }); + } + + useEffect(() => { + handleGetAgencyProducts(); + + return () => {}; + }, [travel_agency_id]) return ( <> - }> + }>
- {/* */}
); From a7c87170a0154d68314944ec7f35965214f54c09 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 20 Jun 2024 14:04:22 +0800 Subject: [PATCH 08/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A2=E6=9C=8D=E9=A6=96=E9=A1=B5,=20=E5=AE=A1?= =?UTF-8?q?=E6=A0=B8=E4=BA=A7=E5=93=81:=20=E6=A0=B8=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SearchForm.jsx | 6 +++--- src/hooks/useProductsSets.js | 25 ++++++++++++++++++++----- src/stores/Products/Index.js | 12 ++++++------ src/views/products/Audit.jsx | 12 +++++++----- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 9f55352..6a53cac 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -15,8 +15,8 @@ import AuditStateSelector from './AuditStateSelector'; //供应商列表 export const fetchVendorList = async () => { - const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-cusservice/PTGetHWVendorList`) - return errcode !== 0 ? [] : Result + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`) + return errcode !== 0 ? [] : result } const { RangePicker } = DatePicker; @@ -212,7 +212,7 @@ function getFields(props) { 'agency', 99, - + , fieldProps?.agency?.col || 6 ), diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index f002991..93feaac 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -3,6 +3,21 @@ import { useTranslation } from 'react-i18next'; /** * 产品管理 相关的预设数据 + * 项目类型 +1 酒店预定 +2 火车 +3 飞机票务 +4 游船 +5 快巴 +6 旅行社(综费) +7 景点 +8 特殊项目 +9 其他 +A 酒店 +B 超公里 +C 餐费 +D 包价包 +X 站 */ export const useProductsTypes = () => { @@ -11,13 +26,13 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ - { label: t('products:type.Experience'), value: 'Experience', key: 'Experience' }, + { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Car'), value: 'Car', key: 'Car' }, { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, - { label: t('products:type.Package'), value: 'Package', key: 'Package' }, - { label: t('products:type.Attractions'), value: 'Attractions', key: 'Attractions' }, - { label: t('products:type.Meals'), value: 'Meals', key: 'Meals' }, - { label: t('products:type.Extras'), value: 'Extras', key: 'Extras' }, + { 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' }, { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 544c478..51d3010 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -10,21 +10,21 @@ export const searchAgencyAction = async (param) => { return errcode !== 0 ? [] : result; }; export const getAgencyProductsAction = async (param) => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products`, param); - return errcode !== 0 ? [] : result; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, param); + return errcode !== 0 ? { agency: {}, products: [] } : result; }; export const postProductsQuoteAudit = async (auditState, quoteRow) => { const postbody = { audit_state: auditState, id: quoteRow.id, - travel_agency_id: quoteRow.info.travel_agency_id, + travel_agency_id: quoteRow.travel_agency_id, }; const formData = new FormData(); Object.keys(postbody).forEach((key) => { formData.append(key, postbody[key]); }); - const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/travel-agency-products-quote-audit`, formData); + const json = await postForm(`${HT_HOST}/Service_BaseInfoWeb/quotation_audit`, formData); return json; // return errcode !== 0 ? {} : result; }; @@ -306,9 +306,9 @@ export const useProductsStore = create( const { setLoading, setActiveAgency, setAgencyProducts } = get(); setLoading(true); const res = await getAgencyProductsAction(param); - const productsData = groupBy(res, row => row.info.type); + const productsData = groupBy(res.products, row => row.info.product_type_id); setAgencyProducts(productsData); - setActiveAgency(res[0].info); + setActiveAgency(res.agency); setLoading(false); }, diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 3a4f3e6..16cefc9 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -9,9 +9,10 @@ import { isEmpty } from '@/utils/commons'; const Header = ({ title, agency, ...props}) => { const { t } = useTranslation(); + const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); const handleAuditItem = (state, row) => { - postProductsQuoteAudit(state, row) + postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { message.success('✔'); @@ -44,11 +45,12 @@ const Header = ({ title, agency, ...props}) => { const PriceTable = ({dataSource, loading}) => { const { t } = useTranslation('products'); + const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); const handleAuditPriceItem = (state, row) => { - postProductsQuoteAudit(state, row) + postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { message.success('✔'); @@ -66,7 +68,7 @@ const PriceTable = ({dataSource, loading}) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, - { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit }) => `${value} ${currency} / ${unit}` }, + { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, // {key: 'price', title: t('Currency'), }, // {key: 'currency', title: t('Currency'), }, // {key: 'unit', title: t('Unit'), }, @@ -88,7 +90,7 @@ const PriceTable = ({dataSource, loading}) => { key: 'state', title: t('State'), render: (_, r) => { - return stateMapVal[`${r.info.audit_state}`]?.label; + return stateMapVal[`${r.audit_state_id}`]?.label; }, }, { @@ -144,7 +146,7 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - const { agencyId: travel_agency_id } = useParams(); + const { travel_agency_id } = useParams(); const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); const handleGetAgencyProducts = () => { From a61ef8882baabc51b483b1b0555644a0bbb39daa Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 09:38:35 +0800 Subject: [PATCH 09/46] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8F=82=E6=95=B0:=20wu=5Fid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useProductsSets.js | 14 +++++++------- src/stores/Auth.js | 1 + src/views/App.jsx | 3 ++- src/views/products/Audit.jsx | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 93feaac..0994b3f 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -9,13 +9,13 @@ import { useTranslation } from 'react-i18next'; 3 飞机票务 4 游船 5 快巴 -6 旅行社(综费) -7 景点 -8 特殊项目 +6 旅行社(综费) - +7 景点 - +8 特殊项目 - 9 其他 A 酒店 -B 超公里 -C 餐费 +B 超公里 - +C 餐费 - D 包价包 X 站 */ @@ -33,7 +33,7 @@ export const useProductsTypes = () => { { 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' }, - { label: t('products:type.Special'), value: 'Special', key: 'Special' }, + // { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); }, [i18n.language]); @@ -51,7 +51,7 @@ export const useProductsAuditStates = () => { { 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: '3', value: '3', label: t('products:auditState.Rejected') }, { key: '1', value: '1', label: t('products:auditState.Published') }, ]; setTypes(newData); diff --git a/src/stores/Auth.js b/src/stores/Auth.js index f7f31f3..5511e6b 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -88,6 +88,7 @@ const useAuthStore = create((set, get) => ({ setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) setStorage(KEY_USER_DETAIL, {username: userDetail.LoginName, travelAgencyName: userDetail.VName}) appendRequestParams('token', loginToken) + appendRequestParams('wu_id', userDetail.LMI_SN) // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) startTokenInterval() }, diff --git a/src/views/App.jsx b/src/views/App.jsx index 0edd221..b61c1ce 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -28,7 +28,7 @@ function App() { const [validateUserPassword, tokenTimeout] = useAuthStore( (state) => [state.validateUserPassword, state.tokenTimeout]) - const { loginToken, userDetail } = usingStorage() + const { loginToken, userDetail, userId } = usingStorage() const noticeUnRead = useNoticeStore((state) => state.noticeUnRead) const href = useHref() @@ -40,6 +40,7 @@ function App() { if (!needToLogin) { appendRequestParams('token', loginToken) + appendRequestParams('wu_id', userId) } useEffect(() => { diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 16cefc9..c548de2 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -96,12 +96,12 @@ const PriceTable = ({dataSource, loading}) => { { title: '', key: 'action', - render: (_, r) => ( + render: (_, r) => r.audit_state_id <= 0 ?( - ), + ) : null, }, ]; return
r.id} />; From 7b2c836e9134c3af90aad7f4f95c6db86a233421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Fri, 21 Jun 2024 15:29:54 +0800 Subject: [PATCH 10/46] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=95=86=E4=BB=B7=E6=A0=BC=E7=AE=A1=E7=90=86=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/common.json | 3 +- src/main.jsx | 3 +- src/views/App.jsx | 1 + src/views/gysgl/Index.jsx | 581 ++++++++++++++++++++++++++++ src/views/gysgl/components/date.jsx | 86 ++++ 5 files changed, 672 insertions(+), 2 deletions(-) create mode 100644 src/views/gysgl/Index.jsx create mode 100644 src/views/gysgl/components/date.jsx diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index d689719..f99dd71 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -52,7 +52,8 @@ "Notice": "通知", "Report": "质量评分", "Airticket": "机票订票", - "Products": "产品管理" + "Products": "产品管理", + "gysgl": "供应商价格管理" }, "Validation": { "Title": "温馨提示", diff --git a/src/main.jsx b/src/main.jsx index 0f7c4fc..d0b8310 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -31,7 +31,7 @@ import InvoicePaid from "@/views/invoice/Paid"; import InvoicePaidDetail from "@/views/invoice/PaidDetail"; import Airticket from "@/views/airticket/Index"; import ProductsIndex from '@/views/products/Index'; - +import Gysgl from "@/views/gysgl/Index" import './i18n'; configure({ @@ -67,6 +67,7 @@ const router = createBrowserRouter([ { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, { path: "products",element:}, + { path: "gysgl",element:}, ] }, { diff --git a/src/views/App.jsx b/src/views/App.jsx index cbb2949..3678318 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -135,6 +135,7 @@ function App() { ), }, + {key: 'gysgl', label:{t('menu.gysgl')}} ]} /> diff --git a/src/views/gysgl/Index.jsx b/src/views/gysgl/Index.jsx new file mode 100644 index 0000000..46fb28a --- /dev/null +++ b/src/views/gysgl/Index.jsx @@ -0,0 +1,581 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree } from 'antd'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import DateComponent from './components/date'; +import { fetchJSON } from "@/utils/request"; + +const cpxm = [ + { code: "code", name: "简码" }, + { code: "city_name", name: "城市" }, + { code: "remarks", name: "备注" }, + { code: "open_hours", name: "游览时间" }, + { code: "recommends_rate", name: "推荐指数" } +]; + +const initialData = [ + { key: '1', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, + { key: '2', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, + { key: '3', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, + { key: '4', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, +]; + +const EditableCell = ({ editing, dataIndex, title, inputType, record, children, handleDateSelect, ...restProps }) => { + let inputNode = inputType === 'number' ? : ; + if (dataIndex === 'yxq' && editing) { + return ( + + ); + } + + return ( + + ); +}; + +function Index() { + const { t, i18n } = useTranslation(); + const [form] = Form.useForm(); + const [quotation, setQuotation] = useState(initialData); + const [editingKey, setEditingKey] = useState(''); + const [tags, setTags] = useState(['中文', 'English']); + const [isModalVisible, setIsModalVisible] = useState(false); + const [selectedTag, setSelectedTag] = useState('中文'); + const [saveData, setSaveData] = useState(null); + const [datePickerVisible, setDatePickerVisible] = useState(false); + const [currentKey, setCurrentKey] = useState(null); + const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); + const [formState, setFormState] = useState({}); + const [selectedNodeKey, setSelectedNodeKey] = useState(null); + + const isEditing = (record) => record.key === editingKey; + + const edit = (record) => { + form.setFieldsValue({ ...record }); + setEditingKey(record.key); + }; + + const cancel = () => { + setEditingKey(''); + }; + + const handleSave = async (key) => { + try { + const row = await form.validateFields(); + const newData = [...quotation]; + + const index = newData.findIndex((item) => key === item.key); + if (index > -1) { + const item = newData[index]; + newData.splice(index, 1, { ...item, ...row }); + setQuotation(newData); + setEditingKey(''); + } else { + newData.push(row); + setQuotation(newData); + setEditingKey(''); + } + } catch (errInfo) { + console.log('Validate Failed:', errInfo); + } + }; + + const handleDelete = (key) => { + const newData = [...quotation]; + const index = newData.findIndex((item) => key === item.key); + newData.splice(index, 1); + setQuotation(newData); + }; + + const handleAdd = () => { + const newData = { + key: `${quotation.length + 1}`, + jg: '', + bz: '', + lx: '', + zm: '', + rq: '', + rd: '', + yxq: '', + }; + setQuotation([...quotation, newData]); + }; + + const handleDateSelect = (key) => { + setCurrentKey(key); + setDatePickerVisible(true); + }; + + const handleDateChange = (date) => { + if (currentKey) { + const newData = [...quotation]; + const index = newData.findIndex((item) => currentKey === item.key); + if (index > -1) { + newData[index].yxq = date; + setQuotation(newData); + setCurrentKey(null); + setDatePickerVisible(false); + } + } + }; + + const columns = [ + { title: '成人价', dataIndex: 'crj', width: '10%', editable: true }, + { title: '儿童价', dataIndex: 'etj', width: '10%', editable: true }, + { title: '币种', dataIndex: 'bz', width: '10%', editable: true }, + { title: '类型', dataIndex: 'lx', width: '10%', editable: true }, + { title: '人群', dataIndex: 'rq', width: '10%', editable: true }, + { title: '人等', dataIndex: 'rd', width: '10%', editable: true }, + { title: '有效期', dataIndex: 'yxq', width: '30%', editable: true }, + { + title: '操作', + dataIndex: 'operation', + render: (_, record) => { + const editable = isEditing(record); + return editable ? ( + + handleSave(record.key)} style={{ marginRight: 8 }}>保存 + 取消 + + ) : ( + + edit(record)} style={{ marginRight: 8 }}>编辑 + handleDelete(record.key)}> + 删除 + + + ); + }, + }, + ]; + + + const mergedColumns = columns.map((col) => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: (record) => ({ + record, + inputType: col.dataIndex === 'age' ? 'number' : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditing(record), + handleDateSelect: handleDateSelect, + }), + }; + }); + + const handleTagClick = (tag) => { + setSelectedTag(tag); + + }; + + const showModal = () => setIsModalVisible(true); + + const handleOk = () => setIsModalVisible(false); + + const handleCancel = () => setIsModalVisible(false); + + + const handleTagChange = (value) => { + if (!tags.includes(value)) { + setTags([...tags, value]); + setLanguageStatus([...languageStatus, { [value]: { title: "", description: "" } }]); + } + setSelectedTag(value); + setIsModalVisible(false); + }; + + const handleChange = (field, value) => { + const updatedLanguageStatus = languageStatus.map(lang => { + if (lang[selectedTag]) { + return { ...lang, [selectedTag]: { ...lang[selectedTag], [field]: value } }; + } + return lang; + }); + setLanguageStatus(updatedLanguageStatus); + }; + + const findLanguageDetails = (tag) => { + const lang = languageStatus.find(lang => lang[tag]); + return lang ? lang[tag] : { title: "", description: "" }; + }; + + + //树组件方法 + const handleNodeSelect = async (_, { node }) => { + // 保存当前表单数据 + if (selectedNodeKey) { + await handleSaveForm(); + } + + // 更新选中的节点 + setSelectedNodeKey(node.key); + + // 加载新节点的表单数据 + if (formState[node.key]) { + form.setFieldsValue(formState[node.key]); + setQuotation(formState[node.key].quotation || []); + setLanguageStatus(formState[node.key].lgc_details || languageStatus); + } else { + form.resetFields(); + setQuotation(initialData); + setLanguageStatus([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); + } + }; + + const handleSaveForm = async () => { + try { + const values = await form.validateFields(); + const newFormState = { + ...formState, + [selectedNodeKey]: { + ...values, + quotation, + lgc_details: languageStatus, + }, + }; + setFormState(newFormState); + } catch (error) { + console.error('Validation failed:', error); + } + }; + + + const onSave = (values) => { + const tempData = values; + tempData['quotation'] = quotation; + tempData['extras'] = quotationBdcp; + tempData['lgc_details'] = languageStatus; + setSaveData(tempData); + console.log("保存的数据",tempData) + }; + + + //绑定产品 + const initialDataBdcp = [ + { key: '1', mc: '15', jg: "美元", lx: 'aa' }, + { key: '2', mc: '15', jg: "美元", lx: 'aa' }, + { key: '3', mc: '15', jg: "美元", lx: 'aa' }, + { key: '4', mc: '15', jg: "美元", lx: 'aa' }, + ] + // const [form] = Form.useForm(); + const [quotationBdcp, setQuotationBdcp] = useState(initialDataBdcp); + const isEditingBdcp = (record) => record.key === editingKeyBdcp; + const [editingKeyBdcp, setEditingKeyBdcp] = useState(''); + + const editBdcp = (record) => { + form.setFieldsValue({ ...record }); + setEditingKeyBdcp(record.key); + }; + const cancelBdcp = () => { + setEditingKeyBdcp(''); + }; + const EditableCellBdcp = ({ + editing, + dataIndex, + title, + inputType, + record, + index, + children, + ...restProps + }) => { + const inputNode = inputType === 'number' ? : ; + return ( + + ); + }; + const bdcpColums = [ + { title: '名称', dataIndex: 'mc', width: '15%', editable: true }, + { title: '价格', dataIndex: 'jg', width: '15%', editable: true }, + { title: '类型', dataIndex: 'lx', width: '40%', editable: true }, + { + title: '操作', + dataIndex: 'operation', + render: (_, record) => { + const editable = isEditingBdcp(record); + return editable ? ( + + handleSaveBdcp(record.key)} style={{ marginRight: 8 }}>保存 + 取消 + + ) : ( + + editBdcp(record)} style={{ marginRight: 8 }}>编辑 + handleDeleteBdcp(record.key)}> + 删除 + + + ); + }, + }, + ].map(col => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: record => ({ + record, + inputType: col.dataIndex === 'jg' ? 'number' : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditingBdcp(record), + }), + }; + }); + + + + const handleAddBdcp = () => { + const newData = { + key: `${quotationBdcp.length + 1}`, + mc: '', + jg: '', + lx: '', + }; + setQuotationBdcp([...quotationBdcp, newData]); + setEditingKeyBdcp(''); // 添加这一行 + }; + + const handleSaveBdcp = async (key) => { + try { + const row = await form.validateFields(); + const newData = [...quotationBdcp]; + + const index = newData.findIndex((item) => key === item.key); + if (index > -1) { + const item = newData[index]; + newData.splice(index, 1, { ...item, ...row }); + setQuotationBdcp(newData); + setEditingKeyBdcp(''); + } else { + newData.push(row); + setQuotationBdcp(newData); + setEditingKeyBdcp(''); + } + } catch (errInfo) { + console.log('Validate Failed:', errInfo); + } + }; + + + const handleDeleteBdcp = (key) => { + const newData = [...quotationBdcp]; + const index = newData.findIndex((item) => key === item.key); + newData.splice(index, 1); + setQuotationBdcp(newData); + }; + + const componentsBdcp = { + body: { + cell: EditableCellBdcp, + }, + }; + + + //Effect + useEffect(() => { + if (saveData) { + } + }, [saveData]); + + useEffect(() => { + // const { errcode, result } = fetchJSON('http://127.0.0.1:4523/m1/2602949-1933890-default/Service_BaseInfoWeb/travel_agency_products'); + console.log("get请求") + }, []) + + useEffect(() => { + if (selectedNodeKey) { + handleSaveForm(); + } + }, [selectedNodeKey]); + + + return ( +
+ +
+ + + + + + + + 供应商 }, + { title: 综费 }, + { title: '文章列表' } + ]} /> + } + > +

产品项目

+ + {cpxm.map((item, index) => ( +
+ + + + + ))} + + + + {tags.map(tag => ( + handleTagClick(tag)} + color={tag === selectedTag ? 'blue' : undefined} + style={{ cursor: 'pointer' }} + > + {tag} + + ))} + + + + }> + + + + + + handleChange('title', e.target.value)} + /> + + + handleChange('description', e.target.value)} + /> + + + + + +

供应商报价

+ +
+ {children} + + + {editing ? ( + + {inputNode} + + ) : ( + children + )} + + {editing ? ( + + {inputNode} + + ) : ( + children + )} +
+ + + + + +

绑定产品

+ +
+ + + + + + + + + + + {datePickerVisible && ( + setDatePickerVisible(false)} + onCancel={() => setDatePickerVisible(false)} + > + + + )} + + ); +} +export default Index; + + diff --git a/src/views/gysgl/components/date.jsx b/src/views/gysgl/components/date.jsx new file mode 100644 index 0000000..51bc9ca --- /dev/null +++ b/src/views/gysgl/components/date.jsx @@ -0,0 +1,86 @@ +import React, { useState } from 'react'; +import { DatePicker, Input, Button } from 'antd'; +import dayjs from 'dayjs'; + +const DateComponent = ({ onDateChange }) => { + const dateFormat = 'YYYY/MM/DD'; + const { RangePicker } = DatePicker; + + const [selectedMonths, setSelectedMonths] = useState([]); + const [selectedDays, setSelectedDays] = useState([]); + + const handleChange = (date, dateString) => { + const dateFw = dateString[0] + "-" + dateString[1]; + onDateChange(dateFw); + }; + + const months = [ + 'January', 'February', 'March', 'April', 'May', 'June', + 'July', 'August', 'September', 'October', 'November', 'December' + ]; + + const days = [ + 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' + ]; + + const handleMonthClick = (month) => { + setSelectedMonths((prevSelectedMonths) => { + if (prevSelectedMonths.includes(month)) { + return prevSelectedMonths.filter((m) => m !== month); + } else { + return [...prevSelectedMonths, month]; + } + }); + }; + + const handleDayClick = (day) => { + setSelectedDays((prevSelectedDays) => { + if (prevSelectedDays.includes(day)) { + return prevSelectedDays.filter((d) => d !== day); + } else { + return [...prevSelectedDays, day]; + } + }); + }; + + return ( +
+

Title

+ +

Data

+ +

Months

+
+ {months.map((month, index) => ( + + ))} +
+

Weekdays

+
+ {days.map((day, index) => ( + + ))} +
+
+ ); +}; + +export default DateComponent; From e9b7aec5219c997bc6acce03d232c99d83237ecb Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 14:20:06 +0800 Subject: [PATCH 11/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E6=90=9C=E7=B4=A2+=E5=B9=B4=E4=BB=BD,=E7=8A=B6?= =?UTF-8?q?=E6=80=81;=20=E4=BA=A7=E5=93=81=E7=B1=BB=E5=9E=8B=E6=8E=92?= =?UTF-8?q?=E5=BA=8F;=20=E5=AE=A1=E6=A0=B8=E9=A1=B5:=20=E6=88=90=E4=BA=BA,?= =?UTF-8?q?=E5=84=BF=E7=AB=A5=E4=BB=B7;=20=E8=AF=AD=E7=A7=8D=E5=88=97?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 5 +- public/locales/zh/products.json | 7 +- src/components/SearchForm.jsx | 35 ++- src/components/SearchInput.jsx | 1 + .../{usePresets.js => useDatePresets.js} | 4 +- src/hooks/useLanguageSets.js | 14 ++ src/hooks/useProductsSets.js | 1 + src/main.jsx | 4 +- src/stores/Products/Index.js | 236 +----------------- src/views/products/Audit.jsx | 51 ++-- src/views/products/Index.jsx | 27 +- 11 files changed, 111 insertions(+), 274 deletions(-) rename src/hooks/{usePresets.js => useDatePresets.js} (91%) create mode 100644 src/hooks/useLanguageSets.js diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 6033e89..b5e7694 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -7,6 +7,7 @@ "Attractions": "Attractions", "Meals": "Meals", "Extras": "Extras", + "Overtravel": "超公里", "Special": "Special" }, "auditState": { @@ -20,7 +21,7 @@ "New": "New", "Pending": "Pending", "Approved": "Approve", - "Rejected": "Rejecte", + "Rejected": "Reject", "Published": "Publish" }, "Status": "Status", @@ -42,6 +43,8 @@ "UseDates": "Use Dates", "Weekdays": "Weekdays", + "UseYear": "Use Year", + "AgeType": { "Type": "Age Type", "Adult": "Adult", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 01f7836..9f1f8b6 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -6,7 +6,8 @@ "Package": "包价线路", "Attractions": "景点", "Meals": "餐费", - "Extras": "附加", + "Extras": "附加项目", + "Overtravel": "超公里", "Special": "特殊项目" }, "auditState": { @@ -21,7 +22,7 @@ "Pending": "待审核", "Approved": "审核通过", "Rejected": "审核拒绝", - "Published": "发布上线" + "Published": "审核发布" }, "Status": "状态", "State": "状态", @@ -41,6 +42,8 @@ "UseDates": "使用日期", "Weekdays": "有效日/周X", + "UseYear": "年份", + "AgeType": { "Type": "人群", "Adult": "成人", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 6a53cac..a669294 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -3,7 +3,7 @@ import { Form, Input, Row, Col, Select, DatePicker, Space, Button } from 'antd'; import { objectMapper, at } from '@/utils/commons'; import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config'; import useFormStore from '@/stores/Form'; -import usePresets from '@/hooks/usePresets'; +import useDatePresets from '@/hooks/useDatePresets'; import { useTranslation } from 'react-i18next'; import { fetchJSON } from '@/utils/request'; @@ -14,8 +14,8 @@ import AuditStateSelector from './AuditStateSelector'; //供应商列表 -export const fetchVendorList = async () => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`) +export const fetchVendorList = async (q) => { + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/VendorList`, { q }) return errcode !== 0 ? [] : result } @@ -23,7 +23,7 @@ const { RangePicker } = DatePicker; const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { const { t } = useTranslation(); - const presets = usePresets(); + const presets = useDatePresets(); const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]); const [formValuesToSub, setFormValuesToSub] = useFormStore((state) => [state.formValuesToSub, state.setFormValuesToSub]); const [form] = Form.useForm(); @@ -57,6 +57,9 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => { return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : ''; }, }, + 'year': [ + { key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') }, + ], }; let dest = {}; const { dates, ...omittedValue } = values; @@ -165,15 +168,15 @@ function getFields(props) { 'referenceNo', 99, - + , fieldProps?.referenceNo?.col || 6 ), item( 'PNR', 99, - - + + , fieldProps?.PNR?.col || 4 ), @@ -208,11 +211,26 @@ function getFields(props) { /** * */ + item( + 'year', + 99, + + + , + fieldProps?.year?.col || 3 + ), item( 'agency', 99, - + , fieldProps?.agency?.col || 6 ), @@ -224,7 +242,6 @@ function getFields(props) { , fieldProps?.audit_state?.col || 3 ), - ]; baseChildren = baseChildren .map((x) => { diff --git a/src/components/SearchInput.jsx b/src/components/SearchInput.jsx index d816647..dcf2657 100644 --- a/src/components/SearchInput.jsx +++ b/src/components/SearchInput.jsx @@ -31,6 +31,7 @@ function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) { showSearch allowClear maxTagCount={1} + dropdownStyle={{width: '16rem'}} {...props} onSearch={debounceFetcher} notFoundContent={fetching ? : null} diff --git a/src/hooks/usePresets.js b/src/hooks/useDatePresets.js similarity index 91% rename from src/hooks/usePresets.js rename to src/hooks/useDatePresets.js index aa046e6..87397b9 100644 --- a/src/hooks/usePresets.js +++ b/src/hooks/useDatePresets.js @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import dayjs from "dayjs"; import { useTranslation } from 'react-i18next'; -const usePresets = () => { +const useDatePresets = () => { const [presets, setPresets] = useState([]); const { t, i18n } = useTranslation(); @@ -39,4 +39,4 @@ const usePresets = () => { return presets; } -export default usePresets; +export default useDatePresets; diff --git a/src/hooks/useLanguageSets.js b/src/hooks/useLanguageSets.js new file mode 100644 index 0000000..1dab4b1 --- /dev/null +++ b/src/hooks/useLanguageSets.js @@ -0,0 +1,14 @@ +export const useLanguageSets = () => { + const newData = [ + { key: '1', value: '1', label: 'English' }, + { key: '2', value: '2', label: 'Chinese (中文)' }, + { key: '3', value: '3', label: 'Japanese (日本語)' }, + { key: '4', value: '4', label: 'German (Deutsch)' }, + { key: '5', value: '5', label: 'French (Français)' }, + { key: '6', value: '6', label: 'Spanish (Español)' }, + { key: '7', value: '7', label: 'Russian (Русский)' }, + { key: '8', value: '8', label: 'Italian (Italiano)' }, + ]; + + return newData; +}; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 0994b3f..5f3a85b 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -27,6 +27,7 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, + { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, { label: t('products:type.Car'), value: 'Car', key: 'Car' }, { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, { label: t('products:type.Package'), value: 'D', key: 'D' }, diff --git a/src/main.jsx b/src/main.jsx index 3733462..a4556b5 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -69,8 +69,8 @@ const router = createBrowserRouter([ { path: "invoice/paid/detail/:flid",element:}, { path: "airticket",element:}, { path: "products",element:}, - { path: "products/:travel_agency_id/audit",element:}, - { path: "products/:travel_agency_id",element:}, + { path: "products/:travel_agency_id/:use_year/:audit_state/audit",element:}, + { path: "products/:travel_agency_id/:use_year/:audit_state/edit",element:}, ] }, { diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 51d3010..a18f098 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -10,7 +10,8 @@ export const searchAgencyAction = async (param) => { return errcode !== 0 ? [] : result; }; export const getAgencyProductsAction = async (param) => { - const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, param); + const _param = { ...param, use_year: (param.use_year || '').replace('all', ''), audit_state: (param.audit_state || '').replace('all', '') }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, _param); return errcode !== 0 ? { agency: {}, products: [] } : result; }; @@ -47,238 +48,10 @@ export const postProductsAudit = async (auditState, infoRow) => { const initialState = { loading: false, + searchValues: {}, agencyList: [], activeAgency: {}, - agencyProducts: groupBy([ - { - "info": { - "id": "640000198509289851", - "title": "如拉下完公", - "code": "grlkt", - "type": "Guide", - "audit_state": "1", - "create_date": "2022-01-13", - "created_by": "郝涛", - "travel_agency_id": "710000200712195349", - "travel_agency_name": "国得气验", - "lastedit_memo": "划百引程级门会需代领主属快。", - "remarks": "及决对金利低集小理电和按常如门。", - "duration": 2, - "duration_unit": "m", - "open_weekdays": "6", - "recommends_rate": 3, - "dept": 1, - "display_to_c": "2", - "km": 27, - "city_id": 77, - "city_name": "称命" - }, - "quotation": [ - { - "id": "21000020030611324X", - "value": 70, - "currency": "CNY", - "unit": "团", - "age_type": "儿童", - "group_size_min": 4, - "group_size_max": 4, - "use_dates_start": "2004-01-19", - "use_dates_end": "1990-03-10", - "weekdays": "4", - "audit_state": "ea pariatur", - "lastedit_memo": "sunt" - }, - { - "id": "610000197306240177", - "value": 86, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 6, - "group_size_max": 8, - "use_dates_start": "1996-12-16", - "use_dates_end": "1974-11-19", - "weekdays": "4", - "audit_state": "aliqua aute quis ipsum", - "lastedit_memo": "commodo adipisicing ea ipsum" - } - ], - "lgc_details": [ - { - "lgc": "mollit", - "title": "林运但", - "description": "学克信图走法因心委周说步将且文手越。", - "id": "35" - }, - { - "lgc": "et laborum", - "title": "备上引深量知量", - "description": "到听少文话包由北层中争二调原务越明在。", - "id": "74" - }, - { - "lgc": "minim velit", - "title": "安都始新", - "description": "取影压前手府要青白支大而。", - "id": "23" - } - ] - }, - { - "info": { - "id": "41000019901227754X", - "title": "据划京少国取", - "code": "ore", - "type": "Guide", - "audit_state": "2", - "create_date": "1979-01-31", - "created_by": "陆芳", - "travel_agency_id": "110000198612200137", - "travel_agency_name": "少平酸型", - "lastedit_memo": "八想军也装运知长示各院步济水。", - "remarks": "千改原统实专回列参目党却是样与后收。", - "duration": 3, - "duration_unit": "d", - "open_weekdays": "5", - "recommends_rate": 5, - "dept": 1, - "display_to_c": "2", - "km": 30, - "city_id": 62, - "city_name": "业入" - }, - "quotation": [ - { - "id": "37000019760525515X", - "value": 93, - "currency": "CNY", - "unit": "团", - "age_type": "成人", - "group_size_min": 7, - "group_size_max": 11, - "use_dates_start": "1992-11-22", - "use_dates_end": "1997-07-16", - "weekdays": "7", - "audit_state": "id nulla irure cupidatat", - "lastedit_memo": "quis aute reprehenderit consectetur" - }, - { - "id": "150000199506023175", - "value": 90, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 9, - "group_size_max": 10, - "use_dates_start": "2007-09-11", - "use_dates_end": "2013-07-27", - "weekdays": "5", - "audit_state": "commodo ad ut", - "lastedit_memo": "id anim incididunt" - } - ], - "lgc_details": [ - { - "lgc": "adipisicing elit Excepteur in", - "title": "很很结龙认", - "description": "事起复京长立然将采共层列工。", - "id": "43" - }, - { - "lgc": "dolore fugiat", - "title": "专中小", - "description": "示史想当集认点离反而原化精满并计前。", - "id": "28" - }, - { - "lgc": "sunt consectetur ea cillum", - "title": "他率带没", - "description": "节经厂面际是统表王活基书色活至是干验。", - "id": "83" - }, - { - "lgc": "incididunt labore fugiat", - "title": "精话西改", - "description": "须事金性别民学少拉个且须专需断连。", - "id": "97" - }, - { - "lgc": "dolore id", - "title": "文技话", - "description": "上任成条到则查支外很素给务府三。", - "id": "99" - } - ] - }, - { - "info": { - "id": "44000019990112280X", - "title": "节到和", - "code": "ixlmndtmz", - "type": "Meals", - "audit_state": "1", - "create_date": "2006-12-30", - "created_by": "易敏", - "travel_agency_id": "640000197111288408", - "travel_agency_name": "术备带走", - "lastedit_memo": "认队什教调问传改万消然声地全。", - "remarks": "属须厂几问总识看部群该克员方。", - "duration": 2, - "duration_unit": "m", - "open_weekdays": "6", - "recommends_rate": 3, - "dept": 2, - "display_to_c": "1", - "km": 13, - "city_id": 55, - "city_name": "铁以" - }, - "quotation": [ - { - "id": "13000019860219219X", - "value": 88, - "currency": "CNY", - "unit": "团", - "age_type": "儿童", - "group_size_min": 2, - "group_size_max": 4, - "use_dates_start": "1991-03-19", - "use_dates_end": "1974-03-13", - "weekdays": "3", - "audit_state": "officia voluptate ad adipisicing dolore", - "lastedit_memo": "Duis amet veniam enim" - }, - { - "id": "420000201706118123", - "value": 61, - "currency": "CNY", - "unit": "人", - "age_type": "儿童", - "group_size_min": 4, - "group_size_max": 10, - "use_dates_start": "1992-04-23", - "use_dates_end": "1970-07-19", - "weekdays": "5", - "audit_state": "commodo labore", - "lastedit_memo": "ullamco anim culpa do in" - } - ], - "lgc_details": [ - { - "lgc": "ut minim", - "title": "回等这意", - "description": "农满界个整千书得被写况空派会想头无。", - "id": "40" - }, - { - "lgc": "laborum id elit irure commodo", - "title": "增正数白养土子", - "description": "么划才共别程以元于族完难变。", - "id": "84" - } - ] - } -], row => row.info.type), + agencyProducts: {}, }; export const useProductsStore = create( devtools((set, get) => ({ @@ -287,6 +60,7 @@ export const useProductsStore = create( // state actions setLoading: loading => set({ loading }), + setSearchValues: searchValues => set({ searchValues }), setAgencyList: (agencyList) => set({ agencyList }), setActiveAgency: activeAgency => set({ activeAgency }), setAgencyProducts: agencyProducts => set({ agencyProducts }), diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index c548de2..e055cc3 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -7,15 +7,18 @@ import { useTranslation } from 'react-i18next'; import useProductsStore, { postProductsQuoteAudit } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; -const Header = ({ title, agency, ...props}) => { +const Header = ({ title, agency, refresh, ...props}) => { 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}) .then((json) => { if (json.errcode === 0) { - message.success('✔'); + message.success(json.errmsg); + if (typeof refresh === 'function') { + refresh(); + } } }) .catch((ex) => { @@ -34,8 +37,14 @@ const Header = ({ title, agency, ...props}) => { {/* */} {/* */} - + {/* */} + {/* todo: export */} @@ -43,9 +52,9 @@ const Header = ({ title, agency, ...props}) => { ); }; -const PriceTable = ({dataSource, loading}) => { +const PriceTable = ({dataSource,refresh}) => { const { t } = useTranslation('products'); - const [activeAgency, ] = useProductsStore((state) => [state.activeAgency]); + const [loading, activeAgency, ] = useProductsStore((state) => [state.loading, state.activeAgency, ]); const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); @@ -53,7 +62,10 @@ const PriceTable = ({dataSource, loading}) => { postProductsQuoteAudit(state, {id: row.id, travel_agency_id: activeAgency.travel_agency_id}) .then((json) => { if (json.errcode === 0) { - message.success('✔'); + message.success(json.errmsg); + if (typeof refresh === 'function') { + refresh(); + } } }) .catch((ex) => { @@ -68,11 +80,11 @@ const PriceTable = ({dataSource, loading}) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }) }, - { key: 'value', title: t('Quotation'), render: (_, { value, currency, unit_name }) => `${value} ${currency} / ${unit_name}` }, + { 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'), }, // {key: 'currency', title: t('Currency'), }, // {key: 'unit', title: t('Unit'), }, - { key: 'ageType', dataIndex: ['age_type'], title: t('AgeType.Type') }, { key: 'groupSize', dataIndex: ['group_size_min'], @@ -94,24 +106,24 @@ const PriceTable = ({dataSource, loading}) => { }, }, { - title: '', + title: '价格审核', key: 'action', render: (_, r) => r.audit_state_id <= 0 ?( - + ) : null, }, ]; - return
r.id} />; + return
r.id} />; } /** * */ -const TypesPanels = () => { - const [loading, agencyProducts] = useProductsStore((state) => [state.loading, state.agencyProducts]); +const TypesPanels = (props) => { + const [loading, agencyProducts, ] = useProductsStore((state) => [state.loading, state.agencyProducts]); // console.log(agencyProducts); const productsTypes = useProductsTypes(); const [activeKey, setActiveKey] = useState([]); @@ -125,8 +137,9 @@ const TypesPanels = () => { ...ele, children: ( r.concat(c.quotation.map((q, i) => ({ ...q, info: c.info, rowSpan: i === 0 ? c.quotation.length : 0 }))), [])} + refresh={props.refresh} /> ), })); @@ -146,11 +159,11 @@ const TypesPanels = () => { } const Audit = ({ ...props }) => { - const { travel_agency_id } = useParams(); + const { travel_agency_id, use_year, audit_state } = useParams(); const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]); const handleGetAgencyProducts = () => { - getAgencyProducts({ travel_agency_id }); + getAgencyProducts({ travel_agency_id, use_year, audit_state }); } useEffect(() => { @@ -161,9 +174,9 @@ const Audit = ({ ...props }) => { return ( <> - }> + }>
- +
); diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 02d57ec..5a6385c 100644 --- a/src/views/products/Index.jsx +++ b/src/views/products/Index.jsx @@ -11,16 +11,18 @@ import { objectMapper } from '@/utils/commons'; function Index() { const { t } = useTranslation(); const [loading, agencyList, searchAgency] = useProductsStore((state) => [state.loading, state.agencyList, state.searchAgency]); + const [searchValues, setSearchValues] = useProductsStore((state) => [state.searchValues, state.setSearchValues]); const formValuesToSub = useFormStore(state => state.formValuesToSub); const handleSearchAgency = (formVal = undefined) => { const { starttime, endtime, ...param } = formVal || formValuesToSub; const searchParam = objectMapper(param, { agency: 'travel_agency_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + setSearchValues(searchParam); searchAgency(searchParam); } useEffect(() => { - handleSearchAgency(); + // handleSearchAgency(); }, []); const showTotal = (total) => t('Table.Total', { total }); @@ -37,8 +39,8 @@ function Index() { key: 'action', render: (_, r) => ( - {t('Edit')} - {t('Audit')} + {t('Edit')} + {t('Audit')} ), }, @@ -47,19 +49,21 @@ function Index() { { handleSearchAgency(formVal); @@ -67,7 +71,14 @@ function Index() { />
-
+
From cacc4832c5ce355b0bf179cfd6675b5a6c867544 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 21 Jun 2024 15:58:33 +0800 Subject: [PATCH 12/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E6=90=9C=E7=B4=A2:=20=E5=A4=9A=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/products/Index.jsx b/src/views/products/Index.jsx index 5a6385c..d1b1495 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_id', startdate: 'edit_date1', enddate: 'edit_date2' }); + const searchParam = objectMapper(param, { agency: 'travel_agency_ids', startdate: 'edit_date1', enddate: 'edit_date2' }); setSearchValues(searchParam); searchAgency(searchParam); } @@ -49,14 +49,14 @@ function Index() { Date: Mon, 24 Jun 2024 09:25:47 +0800 Subject: [PATCH 13/46] =?UTF-8?q?HT=E8=AF=AD=E7=A7=8D;=20=E5=BD=93?= =?UTF-8?q?=E5=89=8D=E8=AF=AD=E7=A7=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...seLanguageSets.js => useHTLanguageSets.js} | 2 +- src/hooks/useProductsSets.js | 67 ++++++++++++++----- src/i18n/LanguageSwitcher.jsx | 5 ++ 3 files changed, 56 insertions(+), 18 deletions(-) rename src/hooks/{useLanguageSets.js => useHTLanguageSets.js} (90%) diff --git a/src/hooks/useLanguageSets.js b/src/hooks/useHTLanguageSets.js similarity index 90% rename from src/hooks/useLanguageSets.js rename to src/hooks/useHTLanguageSets.js index 1dab4b1..73b827e 100644 --- a/src/hooks/useLanguageSets.js +++ b/src/hooks/useHTLanguageSets.js @@ -1,4 +1,4 @@ -export const useLanguageSets = () => { +export const useHTLanguageSets = () => { const newData = [ { key: '1', value: '1', label: 'English' }, { key: '2', value: '2', label: 'Chinese (中文)' }, diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 5f3a85b..35cd3f5 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -4,20 +4,39 @@ import { useTranslation } from 'react-i18next'; /** * 产品管理 相关的预设数据 * 项目类型 -1 酒店预定 -2 火车 -3 飞机票务 -4 游船 -5 快巴 -6 旅行社(综费) - -7 景点 - -8 特殊项目 - -9 其他 -A 酒店 -B 超公里 - -C 餐费 - -D 包价包 -X 站 +酒店预定 1 +火车 2 +飞机票务 3 +游船 4 +快巴 5 +旅行社(综费) 6 +景点 7 +特殊项目 8 +其他 9 +酒店 A +超公里 B +餐费 C +小包价 D +站 X +购物 S +餐 R +娱乐 E +精华线路 T +客人testimonial F +线路订单 O +省 P +信息 I +国家 G +城市 K +图片 H +地图 M +包价线路 L +节日节庆 V +火车站 N +手机租赁 Z + * * todo: webht 类型 +P 导游 +T 车费 */ export const useProductsTypes = () => { @@ -28,8 +47,8 @@ export const useProductsTypes = () => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, - { label: t('products:type.Car'), value: 'Car', key: 'Car' }, - { label: t('products:type.Guide'), value: 'Guide', key: 'Guide' }, + { label: t('products:type.Car'), value: 'T', key: 'T' }, + { label: t('products:type.Guide'), value: 'P', key: 'P' }, { 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' }, @@ -42,7 +61,6 @@ export const useProductsTypes = () => { return types; }; - export const useProductsAuditStates = () => { const [types, setTypes] = useState([]); const { t, i18n } = useTranslation(); @@ -66,3 +84,18 @@ export const useProductsAuditStatesMapVal = (value) => { const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); return stateMapVal; }; + +export const useProductsTypesFieldsets = (type, role) => { + const infoDefault = ['code', 'title']; + const infoAdmin = ['remarks', 'dept']; + const infoTypesMap = { + '6': [], + 'B': ['city_id', 'km'], + '车费': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], // todo: 包价类型?? + '导游': ['description', 'city_id', 'duration', ], + 'D': [], //todo: 木有图 + '7': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], // todo: 怎么是2个图 + 'C': ['description', 'city_id',], + '8': [], // todo: ? + }; +}; diff --git a/src/i18n/LanguageSwitcher.jsx b/src/i18n/LanguageSwitcher.jsx index af87005..dd1a743 100644 --- a/src/i18n/LanguageSwitcher.jsx +++ b/src/i18n/LanguageSwitcher.jsx @@ -8,6 +8,11 @@ const i18n_to_htcode = { 'en': 1, }; +export const useLanguage = () => { + const { i18n } = useTranslation(); + return { language: i18n.language, }; +}; + /** * 语言选择组件 */ From 219372c06e78eb50ce11aeb78de3206c96d8d12f Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 10:58:14 +0800 Subject: [PATCH 14/46] =?UTF-8?q?=E6=96=B0=E5=A2=9EHT=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B:=20J=20=E8=BD=A6=E8=B4=B9,=20Q=20=E5=AF=BC?= =?UTF-8?q?=E6=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useProductsSets.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 35cd3f5..6749ede 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -34,9 +34,9 @@ import { useTranslation } from 'react-i18next'; 节日节庆 V 火车站 N 手机租赁 Z - * * todo: webht 类型 -P 导游 -T 车费 + * * webht 类型, 20240624 新增HT类型 +Q 导游 +J 车费 */ export const useProductsTypes = () => { @@ -47,8 +47,8 @@ export const useProductsTypes = () => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, - { label: t('products:type.Car'), value: 'T', key: 'T' }, - { label: t('products:type.Guide'), value: 'P', key: 'P' }, + { 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.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'C', key: 'C' }, @@ -72,6 +72,7 @@ export const useProductsAuditStates = () => { { 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') }, + // ELSE 未知 ]; setTypes(newData); }, [i18n.language]); @@ -85,15 +86,18 @@ export const useProductsAuditStatesMapVal = (value) => { return stateMapVal; }; +/** + * @ignore + */ export const useProductsTypesFieldsets = (type, role) => { const infoDefault = ['code', 'title']; - const infoAdmin = ['remarks', 'dept']; + const infoAdmin = ['remarks', 'dept', 'display_to_c']; const infoTypesMap = { '6': [], 'B': ['city_id', 'km'], - '车费': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], // todo: 包价类型?? - '导游': ['description', 'city_id', 'duration', ], - 'D': [], //todo: 木有图 + 'J': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], + 'Q': ['description', 'city_id', 'duration', ], + 'D': ['description', 'city_id', 'recommends_rate','duration',], '7': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], // todo: 怎么是2个图 'C': ['description', 'city_id',], '8': [], // todo: ? From 86242addab2776bbf2ec44555ccc07c6d435d610 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 13:45:29 +0800 Subject: [PATCH 15/46] =?UTF-8?q?=E5=88=A0=E9=99=A4=20Mobx=20=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=9A=84=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 10 - src/stores/Feedback.js | 1 - src/stores/Invoice.js | 368 ------------------------------------ src/views/invoice/Index.jsx | 4 +- 4 files changed, 1 insertion(+), 382 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index d78954e..bd73f53 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,5 +1,4 @@ import React from "react"; -import { configure } from "mobx"; import ReactDOM from "react-dom/client"; import { createBrowserRouter, @@ -41,15 +40,6 @@ import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET } import './i18n'; -configure({ - useProxies: "ifavailable", - enforceActions: "observed", - computedRequiresReaction: true, - observableRequiresReaction: false, - reactionRequiresObservable: true, - disableErrorBoundaries: process.env.NODE_ENV == "production" -}); - const router = createBrowserRouter([ { path: "/", diff --git a/src/stores/Feedback.js b/src/stores/Feedback.js index dd3feb1..9b2356e 100644 --- a/src/stores/Feedback.js +++ b/src/stores/Feedback.js @@ -1,4 +1,3 @@ -import { makeAutoObservable, runInAction } from 'mobx'; import { fetchJSON, postForm } from '@/utils/request'; import { groupBy } from '@/utils/commons'; import * as config from '@/config'; diff --git a/src/stores/Invoice.js b/src/stores/Invoice.js index 814e69f..09ac0f2 100644 --- a/src/stores/Invoice.js +++ b/src/stores/Invoice.js @@ -1,10 +1,5 @@ -import { makeAutoObservable, runInAction } from "mobx"; import { fetchJSON, postForm } from "@/utils/request"; -import { prepareUrl, isNotEmpty, objectMapper } from "@/utils/commons"; import { HT_HOST } from "@/config"; -import { json } from "react-router-dom"; -import * as config from "@/config"; -import dayjs from "dayjs"; import { create } from 'zustand'; import { devtools } from 'zustand/middleware'; @@ -138,366 +133,3 @@ const useInvoiceStore = create( ); export default useInvoiceStore; - -export class Invoice { - constructor(root) { - makeAutoObservable(this, { rootStore: false }); - this.root = root; - } - - invoiceList = []; //账单列表 - invoicekImages = []; //图片列表 - invoiceGroupInfo = {}; //账单详细 - invoiceProductList = []; //账单细项 - invoiceZDDetail = []; //报账信息 - invoiceCurrencyList = []; //币种 - invoicePicList = []; //多账单图片列表数组 - invoiceFormData = { info_money: 0, info_Currency: "", info_date: "" }; //存储form数据 - - invoicePaid = [] ; //支付账单列表 - invoicePaidDetail = []; //每期账单详细 - - loading = false; - search_date_start = dayjs().subtract(2, "M").startOf("M"); - search_date_end = dayjs().endOf("M"); - - onDateRangeChange = dates => { - console.log(dates); - this.search_date_start = dates==null? null: dates[0]; - this.search_date_end = dates==null? null: dates[1]; - }; - - fetchInvoiceList(VEI_SN, GroupNo, DateStart, DateEnd,OrderType) { - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-cusservice/PTSearchGMBPageList") - .append("VEI_SN", VEI_SN) - .append("OrderType", 0) - .append("GroupNo", GroupNo.trim()) - .append("DateStart", DateStart) - .append("DateEnd", DateEnd) - .append("Orderbytype", 1) - .append("TimeType", 0) - .append("limitmarket", "") - .append("mddgroup", "") - .append("SecuryGroup", "") - .append("TotalNum", 0) - .append("PageSize", 2000) - .append("PageIndex", 1) - .append("PayState",OrderType) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoiceList = json.Result.map((data, index) => { - return { - key: data.GMDSN, - gmd_gri_sn: data.GMD_GRI_SN, - gmd_vei_sn: data.GMD_VEI_SN, - GetGDate: data.GetGDate, - GMD_FillWorkers_SN: data.GMD_FillWorkers_SN, - GMD_FWks_LastEditTime: data.GMD_FWks_LastEditTime, - GMD_VerifyUser_SN: data.GMD_VerifyUser_SN, - GMD_Dealed: data.GMD_Dealed, - GMD_VRequestVerify: data.GMD_VRequestVerify, - LeftGDate: data.LeftGDate, - GMD_FillWorkers_Name: data.GMD_FillWorkers_Name, - GroupName: data.GroupName, - AllMoney: data.AllMoney, - PersonNum: data.PersonNum, - GMD_Currency: data.GMD_Currency, - VName: data.VName, - FKState: data.FKState, - }; - }); - } else { - this.invoiceList = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - } - - fetchInvoiceDetail(GMDSN, GSN) { - const fetchUrl = prepareUrl(HT_HOST + "/service-cusservice/PTGetZDDetail") - .append("VEI_SN", this.root.authStore.login.travelAgencyId) - .append("GRI_SN", GSN) - .append("GMD_SN", GMDSN) - .append("LGC", 1) - .append("Bill", 1) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - if (json.errcode == 0) { - this.invoiceGroupInfo = json.GroupInfo[0]; - this.invoiceProductList = json.ProductList; - this.invoiceCurrencyList = json.CurrencyList; - this.invoiceZDDetail = json.ZDDetail; - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - return json; - }); - } - - //获取供应商提交的图片 - getInvoicekImages(VEI_SN, GRI_SN) { - let url = `/service-fileServer/ListFile`; - url += `?GRI_SN=${GRI_SN}&VEI_SN=${VEI_SN}&FilePathName=invoice`; - url += `&token=${this.root.authStore.login.token}`; - fetch(config.HT_HOST + url) - .then(response => response.json()) - .then(json => { - console.log(json); - runInAction(() => { - this.invoicekImages = json.result.map((data, index) => { - return { - uid: -index, //用负数,防止添加删除的时候错误 - name: data.file_name, - status: "done", - url: data.file_url, - }; - }); - }); - }) - .catch(error => { - console.log("fetch data failed", error); - }); - } - - //从数据库获取图片列表 - getInvoicekImages_fromData(jsonData) { - let arrLen = jsonData.length; - let arrPicList = jsonData.map((data, index) => { - const GMD_Pic = data.GMD_Pic; - let picList = []; - if (isNotEmpty(GMD_Pic)) { - let js_Pic = JSON.parse(GMD_Pic); - picList = js_Pic.map((picData, pic_Index) => { - return { - uid: -pic_Index, //用负数,防止添加删除的时候错误 - name: "", - status: "done", - url: picData.url, - }; - }); - } - if (data.GMD_Dealed == false && arrLen == index + 1) { - this.invoicekImages = picList; - } - return picList; - }); - - runInAction(() => { - this.invoicePicList = arrPicList; - }); - } - - //获取数据库的表单默认数据回填。 - getFormData(jsonData) { - let arrLen = jsonData.length; - return jsonData.map((data, index) => { - if (data.GMD_Dealed == false && arrLen == index + 1) { - //只有最后一条账单未审核通过才显示 - runInAction(() => { - this.invoiceFormData = { info_money: data.GMD_Cost, info_Currency: data.GMD_Currency, info_date: isNotEmpty(data.GMD_PayDate) ? dayjs(data.GMD_PayDate) : "" }; - }); - } - }); - } - - removeFeedbackImages(fileurl) { - let url = `/service-fileServer/FileDelete`; - url += `?fileurl=${fileurl}`; - url += `&token=${this.root.authStore.login.token}`; - return fetch(config.HT_HOST + url) - .then(response => response.json()) - .then(json => { - console.log(json); - return json.Result; - }) - .catch(error => { - console.log("fetch data failed", error); - }); - } - - postEditInvoiceDetail(GMD_SN, Currency, Cost, PayDate, Pic, Memo) { - let postUrl = HT_HOST + "/service-cusservice/EditSupplierFK"; - let formData = new FormData(); - formData.append("LMI_SN", this.root.authStore.login.userId); - formData.append("GMD_SN", GMD_SN); - formData.append("Currency", Currency); - formData.append("Cost", Cost); - formData.append("PayDate", isNotEmpty(PayDate) ? PayDate : ""); - formData.append("Pic", Pic); - formData.append("Memo", Memo); - formData.append("token",this.root.authStore.login.token); - - return postForm(postUrl, formData).then(json => { - console.info(json); - return json; - }); - } - - postAddInvoice(GRI_SN, Currency, Cost, PayDate, Pic, Memo) { - let postUrl = HT_HOST + "/service-cusservice/AddSupplierFK"; - let formData = new FormData(); - formData.append("LMI_SN", this.root.authStore.login.userId); - formData.append("VEI_SN", this.root.authStore.login.travelAgencyId); - formData.append("GRI_SN", GRI_SN); - formData.append("Currency", Currency); - formData.append("Cost", Cost); - formData.append("PayDate", isNotEmpty(PayDate) ? PayDate : ""); - formData.append("Pic", Pic); - formData.append("Memo", Memo); - formData.append("token",this.root.authStore.login.token); - return postForm(postUrl, formData).then(json => { - console.info(json); - return json; - }); - } - - //账单状态 - invoiceStatus(FKState) { - switch (FKState - 1) { - case 1: - return "Submitted"; - break; - case 2: - return "Travel Advisor"; - break; - case 3: - return "Finance Dept"; - break; - case 4: - return "Paid"; - break; - default: - return ""; - break; - } - } - - fetchInvoicePaid(VEI_SN, GroupNo, DateStart, DateEnd) { - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-Cooperate/Cooperate/GetInvoicePaid") - .append("VEI_SN", VEI_SN) - .append("GroupNo", GroupNo) - .append("DateStart", DateStart) - .append("DateEnd", DateEnd) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoicePaid = json.Result.map((data, index) => { - return { - key: data.fl_id, - fl_finaceNo: data.fl_finaceNo, - fl_vei_sn: data.fl_vei_sn, - fl_year: data.fl_year, - fl_month: data.fl_month, - fl_memo: data.fl_memo, - fl_adddate: data.fl_adddate, - fl_addUserSn: data.fl_addUserSn, - fl_updateUserSn: data.fl_updateUserSn, - fl_updatetime: data.fl_updatetime, - fl_state: data.fl_state, - fl_paid: data.fl_paid, - fl_pic: data.fl_pic, - fcount: data.fcount, - pSum: data.pSum, - }; - }); - } else { - this.invoicePaid = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - - } - - - fetchInvoicePaidDetail(VEI_SN,FLID){ - this.loading = true; - const fetchUrl = prepareUrl(HT_HOST + "/service-Cooperate/Cooperate/GetInvoicePaidDetail") - .append("VEI_SN", VEI_SN) - .append("fl_id", FLID) - .append("token",this.root.authStore.login.token) - .build(); - - return fetchJSON(fetchUrl).then(json => { - runInAction(() => { - this.loading = false; - if (json.errcode == 0) { - if (isNotEmpty(json.Result)) { - this.invoicePaidDetail = json.Result.map((data, index) => { - return { - key: data.fl2_id, - fl2_fl_id: data.fl2_fl_id, - fl2_GroupName: data.fl2_GroupName, - fl2_gri_sn: data.fl2_gri_sn, - fl2_gmd_sn: data.fl2_gmd_sn, - fl2_wl: data.fl2_wl, - fl2_ArriveDate: data.fl2_ArriveDate, - fl2_price: data.fl2_price, - fl2_state: data.fl2_state, - fl2_updatetime: data.fl2_updatetime, - fl2_updateUserSn: data.fl2_updateUserSn, - fl2_memo: data.fl2_memo, - fl2_memo2: data.fl2_memo2, - fl2_paid: data.fl2_paid, - fl2_pic: data.fl2_pic, - }; - }); - } else { - this.invoicePaidDetail = []; - } - } else { - throw new Error(json.errmsg + ": " + json.errcode); - } - }); - }); - } - - /* 测试数据 */ - //账单列表范例数据 - testData = [ - { - GSMSN: 449865, - gmd_gri_sn: 334233, - gmd_vei_sn: 628, - GetDate: "2023-04-2 00:33:33", - GMD_FillWorkers_SN: 8617, - GMD_FWks_LastEditTime: "2023-04-26 12:33:33", - GMD_VerifyUser_SN: 8928, - GMD_Dealed: 1, - GMD_VRequestVerify: 1, - TotalCount: 22, - LeftGDate: "2023-03-30 00:00:00", - GMD_FillWorkers_Name: "", - GroupName: " 中华游230501-CA230402033", - AllMoney: 3539, - FKState: 1, - GMD_Currency: "", - PersonNum: "1大1小", - VName: "", - }, - ]; -} - -// export default Invoice; diff --git a/src/views/invoice/Index.jsx b/src/views/invoice/Index.jsx index 17a9603..559a718 100644 --- a/src/views/invoice/Index.jsx +++ b/src/views/invoice/Index.jsx @@ -1,6 +1,4 @@ import { NavLink, useNavigate } from "react-router-dom"; -import { useState } from "react"; -import { toJS } from "mobx"; import { Row, Col, Space, Button, Table, App, Steps } from "antd"; import { formatDate, isNotEmpty } from "@/utils/commons"; import { AuditOutlined, SmileOutlined, SolutionOutlined, EditOutlined } from "@ant-design/icons"; @@ -103,7 +101,7 @@ function Index() { -
+
From 697fa00be361584dcbe0ad3926b5828a0935e779 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 24 Jun 2024 14:58:14 +0800 Subject: [PATCH 16/46] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=9A=84=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config.js b/src/config.js index fc69f82..03d67fd 100644 --- a/src/config.js +++ b/src/config.js @@ -31,3 +31,10 @@ export const PERM_DOMESTIC = '/domestic/all' // 机票供应商 // category: air-ticket export const PERM_AIR_TICKET = '/air-ticket/all' + +// 价格管理 +export const PERM_PRODUCTS_MANAGEMENT = '/products/*'; // 管理 +export const PERM_PRODUCTS_INFO_AUDIT = '/products/info/audit'; // 信息.审核 +export const PERM_PRODUCTS_INFO_PUT = '/products/info/put'; // 信息.录入 +export const PERM_PRODUCTS_OFFER_AUDIT = '/products/offer/audit'; // 价格.审核 +export const PERM_PRODUCTS_OFFER_PUT = '/products/offer/put'; // 价格.录入 From b0d4943f0986d220d0cf7856293d5c8b79f02c98 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 10:18:08 +0800 Subject: [PATCH 17/46] =?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 18/46] =?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 19/46] =?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 261849fa5f2ebbfc25967c22bf3fe0261a773495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Tue, 25 Jun 2024 11:41:44 +0800 Subject: [PATCH 20/46] =?UTF-8?q?1.=E4=BF=AE=E6=94=B9=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=95=86=E6=8A=A5=E4=BB=B7=E9=A1=B5=E9=9D=A2=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=202.=E5=A2=9E=E5=8A=A0=E6=97=A5=E6=9C=9F=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E5=88=B0components=203.=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 33 +- public/locales/zh/common.json | 3 +- public/locales/zh/products.json | 31 +- src/components/date.jsx | 51 +++ src/main.jsx | 2 - src/views/App.jsx | 1 - src/views/gysgl/Index.jsx | 581 -------------------------- src/views/gysgl/components/date.jsx | 86 ---- src/views/products/Detail.jsx | 607 +++++++++++++++++++++++++++- 9 files changed, 714 insertions(+), 681 deletions(-) create mode 100644 src/components/date.jsx delete mode 100644 src/views/gysgl/Index.jsx delete mode 100644 src/views/gysgl/components/date.jsx diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 2076875..6e50c59 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -22,5 +22,36 @@ "CreatedBy": "Created By", "CreateDate": "Create Date", "AuditedBy": "Audited By", - "AuditDate": "Audit Date" + "AuditDate": "Audit Date", + + + "productProject": "Product project", + "Code": "Code", + "City": "City", + "Remarks": "Remarks", + "tourTime": "Tour time", + "recommendationRate": "Recommends rate", + "Name": "Name", + "Description":"Description", + "supplierQuotation": "Supplier quotation", + "addQuotation": "Add quotation", + "bindingProducts": "Binding products", + "addBinding": "Add binding", + + "adultPrice": "Adult price", + "childrenPrice": "Child price", + "currency": "Currency", + "Types": "Type", + "number": "Number", + "validityPeriod":"Validity period", + "operation": "Operation", + "price": "Price", + + + "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 1a62dc1..4ef0694 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -54,8 +54,7 @@ "Notice": "通知", "Report": "质量评分", "Airticket": "机票订票", - "Products": "产品管理", - "gysgl": "供应商价格管理" + "Products": "产品管理" }, "Validation": { "Title": "温馨提示", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 326bd78..87b9914 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -22,5 +22,34 @@ "CreatedBy": "提交人员", "CreateDate": "提交时间", "Auditors": "审核人员", - "AuditDate": "审核时间" + "AuditDate": "审核时间", + + "productProject": "产品项目", + "Code": "代码", + "City": "城市", + "Remarks": "备注", + "tourTime": "游览时间", + "recommendationRate": "推荐指数", + "Name":"名称", + "Price":"价格", + "Description":"描述", + "supplierQuotation": "供应商报价", + "addQuotation": "添加报价", + "bindingProducts": "绑定产品", + "addBinding": "添加绑定", + + "adultPrice": "成人价", + "childrenPrice": "儿童价", + "currency": "币种", + "Types": "类型", + "number": "人等", + "validityPeriod":"有效期", + "operation": "操作", + "price": "价格", + "save":"保存", + "edit":"编辑", + "cancel":"取消", + "delete":"删除", + "sureCancel": "确定取消?", + "sureDelete":"确定删除?" } diff --git a/src/components/date.jsx b/src/components/date.jsx new file mode 100644 index 0000000..fec0402 --- /dev/null +++ b/src/components/date.jsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import { DatePicker, Button } from 'antd'; + +const DateComponent = ({ onDateChange }) => { + const dateFormat = 'YYYY/MM/DD'; + const { RangePicker } = DatePicker; + const [dateRange, setDateRange] = useState(null); + const [selectedDays, setSelectedDays] = useState([]); + + const days = [ + 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' + ]; + + const handleChange = (date, dateString) => { + const range = dateString[0] + "-" + dateString[1]; + setDateRange(range); + onDateChange({ dateRange: range, selectedDays }); + }; + + const handleDayClick = (day) => { + setSelectedDays((prevSelectedDays) => { + const updatedDays = prevSelectedDays.includes(day) + ? prevSelectedDays.filter((d) => d !== day) + : [...prevSelectedDays, day]; + onDateChange({ dateRange, selectedDays: updatedDays }); + return updatedDays; + }); + }; + + return ( +
+

Data

+ +

Weekdays

+
+ {days.map((day, index) => ( + + ))} +
+
+ ); +}; + +export default DateComponent; diff --git a/src/main.jsx b/src/main.jsx index 6e78774..3ed3c89 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -33,7 +33,6 @@ import Airticket from "@/views/airticket/Index"; import ProductsIndex from '@/views/products/Index'; import ProductsDetail from '@/views/products/Detail'; import ProductsAudit from '@/views/products/Audit'; -import Gysgl from "@/views/gysgl/Index" import './i18n'; configure({ @@ -71,7 +70,6 @@ const router = createBrowserRouter([ { path: "products",element:}, { path: "products/:travel_agency_id/audit",element:}, { path: "products/:travel_agency_id",element:}, - { path: "gysgl",element:}, ] }, { diff --git a/src/views/App.jsx b/src/views/App.jsx index 3678318..cbb2949 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -135,7 +135,6 @@ function App() { ), }, - {key: 'gysgl', label:{t('menu.gysgl')}} ]} /> diff --git a/src/views/gysgl/Index.jsx b/src/views/gysgl/Index.jsx deleted file mode 100644 index d50713f..0000000 --- a/src/views/gysgl/Index.jsx +++ /dev/null @@ -1,581 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree } from 'antd'; -import { Link } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; -import DateComponent from './components/date'; -import { fetchJSON } from "@/utils/request"; - -const cpxm = [ - { code: "code", name: "简码" }, - { code: "city_name", name: "城市" }, - { code: "remarks", name: "备注" }, - { code: "open_hours", name: "游览时间" }, - { code: "recommends_rate", name: "推荐指数" } -]; - -const initialData = [ - { key: '1', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, - { key: '2', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, - { key: '3', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, - { key: '4', crj: '15', etj: "美元", bz: 'aa', lx: 'as', rq: 'dfae', rd: 'dawd', yxq: 'dawda' }, -]; - -const EditableCell = ({ editing, dataIndex, title, inputType, record, children, handleDateSelect, ...restProps }) => { - let inputNode = inputType === 'number' ? : ; - if (dataIndex === 'yxq' && editing) { - return ( -
- ); - } - - return ( - - ); -}; - -function Index() { - const { t, i18n } = useTranslation(); - const [form] = Form.useForm(); - const [quotation, setQuotation] = useState(initialData); - const [editingKey, setEditingKey] = useState(''); - const [tags, setTags] = useState(['中文', 'English']); - const [isModalVisible, setIsModalVisible] = useState(false); - const [selectedTag, setSelectedTag] = useState('中文'); - const [saveData, setSaveData] = useState(null); - const [datePickerVisible, setDatePickerVisible] = useState(false); - const [currentKey, setCurrentKey] = useState(null); - const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); - const [formState, setFormState] = useState({}); - const [selectedNodeKey, setSelectedNodeKey] = useState(null); - - const isEditing = (record) => record.key === editingKey; - - const edit = (record) => { - form.setFieldsValue({ ...record }); - setEditingKey(record.key); - }; - - const cancel = () => { - setEditingKey(''); - }; - - const handleSave = async (key) => { - try { - const row = await form.validateFields(); - const newData = [...quotation]; - - const index = newData.findIndex((item) => key === item.key); - if (index > -1) { - const item = newData[index]; - newData.splice(index, 1, { ...item, ...row }); - setQuotation(newData); - setEditingKey(''); - } else { - newData.push(row); - setQuotation(newData); - setEditingKey(''); - } - } catch (errInfo) { - console.log('Validate Failed:', errInfo); - } - }; - - const handleDelete = (key) => { - const newData = [...quotation]; - const index = newData.findIndex((item) => key === item.key); - newData.splice(index, 1); - setQuotation(newData); - }; - - const handleAdd = () => { - const newData = { - key: `${quotation.length + 1}`, - jg: '', - bz: '', - lx: '', - zm: '', - rq: '', - rd: '', - yxq: '', - }; - setQuotation([...quotation, newData]); - }; - - const handleDateSelect = (key) => { - setCurrentKey(key); - setDatePickerVisible(true); - }; - - const handleDateChange = (date) => { - if (currentKey) { - const newData = [...quotation]; - const index = newData.findIndex((item) => currentKey === item.key); - if (index > -1) { - newData[index].yxq = date; - setQuotation(newData); - setCurrentKey(null); - setDatePickerVisible(false); - } - } - }; - - const columns = [ - { title: '成人价', dataIndex: 'crj', width: '10%', editable: true }, - { title: '儿童价', dataIndex: 'etj', width: '10%', editable: true }, - { title: '币种', dataIndex: 'bz', width: '10%', editable: true }, - { title: '类型', dataIndex: 'lx', width: '10%', editable: true }, - { title: '人群', dataIndex: 'rq', width: '10%', editable: true }, - { title: '人等', dataIndex: 'rd', width: '10%', editable: true }, - { title: '有效期', dataIndex: 'yxq', width: '30%', editable: true }, - { - title: '操作', - dataIndex: 'operation', - render: (_, record) => { - const editable = isEditing(record); - return editable ? ( - - handleSave(record.key)} style={{ marginRight: 8 }}>保存 - 取消 - - ) : ( - - edit(record)} style={{ marginRight: 8 }}>编辑 - handleDelete(record.key)}> - 删除 - - - ); - }, - }, - ]; - - - const mergedColumns = columns.map((col) => { - if (!col.editable) { - return col; - } - return { - ...col, - onCell: (record) => ({ - record, - inputType: col.dataIndex === 'age' ? 'number' : 'text', - dataIndex: col.dataIndex, - title: col.title, - editing: isEditing(record), - handleDateSelect: handleDateSelect, - }), - }; - }); - - const handleTagClick = (tag) => { - setSelectedTag(tag); - - }; - - const showModal = () => setIsModalVisible(true); - - const handleOk = () => setIsModalVisible(false); - - const handleCancel = () => setIsModalVisible(false); - - - const handleTagChange = (value) => { - if (!tags.includes(value)) { - setTags([...tags, value]); - setLanguageStatus([...languageStatus, { [value]: { title: "", description: "" } }]); - } - setSelectedTag(value); - setIsModalVisible(false); - }; - - const handleChange = (field, value) => { - const updatedLanguageStatus = languageStatus.map(lang => { - if (lang[selectedTag]) { - return { ...lang, [selectedTag]: { ...lang[selectedTag], [field]: value } }; - } - return lang; - }); - setLanguageStatus(updatedLanguageStatus); - }; - - const findLanguageDetails = (tag) => { - const lang = languageStatus.find(lang => lang[tag]); - return lang ? lang[tag] : { title: "", description: "" }; - }; - - - //树组件方法 - const handleNodeSelect = async (_, { node }) => { - // 保存当前表单数据 - if (selectedNodeKey) { - await handleSaveForm(); - } - - // 更新选中的节点 - setSelectedNodeKey(node.key); - - // 加载新节点的表单数据 - if (formState[node.key]) { - form.setFieldsValue(formState[node.key]); - setQuotation(formState[node.key].quotation || []); - setLanguageStatus(formState[node.key].lgc_details || languageStatus); - } else { - form.resetFields(); - setQuotation(initialData); - setLanguageStatus([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); - } - }; - - const handleSaveForm = async () => { - try { - const values = await form.validateFields(); - const newFormState = { - ...formState, - [selectedNodeKey]: { - ...values, - quotation, - lgc_details: languageStatus, - }, - }; - setFormState(newFormState); - } catch (error) { - console.error('Validation failed:', error); - } - }; - - - const onSave = (values) => { - const tempData = values; - tempData['quotation'] = quotation; - tempData['extras'] = quotationBdcp; - tempData['lgc_details'] = languageStatus; - setSaveData(tempData); - console.log("保存的数据",tempData) - }; - - - //绑定产品 - const initialDataBdcp = [ - { key: '1', mc: '15', jg: "美元", lx: 'aa' }, - { key: '2', mc: '15', jg: "美元", lx: 'aa' }, - { key: '3', mc: '15', jg: "美元", lx: 'aa' }, - { key: '4', mc: '15', jg: "美元", lx: 'aa' }, - ] - // const [form] = Form.useForm(); - const [quotationBdcp, setQuotationBdcp] = useState(initialDataBdcp); - const isEditingBdcp = (record) => record.key === editingKeyBdcp; - const [editingKeyBdcp, setEditingKeyBdcp] = useState(''); - - const editBdcp = (record) => { - form.setFieldsValue({ ...record }); - setEditingKeyBdcp(record.key); - }; - const cancelBdcp = () => { - setEditingKeyBdcp(''); - }; - const EditableCellBdcp = ({ - editing, - dataIndex, - title, - inputType, - record, - index, - children, - ...restProps - }) => { - const inputNode = inputType === 'number' ? : ; - return ( - - ); - }; - const bdcpColums = [ - { title: '名称', dataIndex: 'mc', width: '15%', editable: true }, - { title: '价格', dataIndex: 'jg', width: '15%', editable: true }, - { title: '类型', dataIndex: 'lx', width: '40%', editable: true }, - { - title: '操作', - dataIndex: 'operation', - render: (_, record) => { - const editable = isEditingBdcp(record); - return editable ? ( - - handleSaveBdcp(record.key)} style={{ marginRight: 8 }}>保存 - 取消 - - ) : ( - - editBdcp(record)} style={{ marginRight: 8 }}>编辑 - handleDeleteBdcp(record.key)}> - 删除 - - - ); - }, - }, - ].map(col => { - if (!col.editable) { - return col; - } - return { - ...col, - onCell: record => ({ - record, - inputType: col.dataIndex === 'jg' ? 'number' : 'text', - dataIndex: col.dataIndex, - title: col.title, - editing: isEditingBdcp(record), - }), - }; - }); - - - - const handleAddBdcp = () => { - const newData = { - key: `${quotationBdcp.length + 1}`, - mc: '', - jg: '', - lx: '', - }; - setQuotationBdcp([...quotationBdcp, newData]); - setEditingKeyBdcp(''); // 添加这一行 - }; - - const handleSaveBdcp = async (key) => { - try { - const row = await form.validateFields(); - const newData = [...quotationBdcp]; - - const index = newData.findIndex((item) => key === item.key); - if (index > -1) { - const item = newData[index]; - newData.splice(index, 1, { ...item, ...row }); - setQuotationBdcp(newData); - setEditingKeyBdcp(''); - } else { - newData.push(row); - setQuotationBdcp(newData); - setEditingKeyBdcp(''); - } - } catch (errInfo) { - console.log('Validate Failed:', errInfo); - } - }; - - - const handleDeleteBdcp = (key) => { - const newData = [...quotationBdcp]; - const index = newData.findIndex((item) => key === item.key); - newData.splice(index, 1); - setQuotationBdcp(newData); - }; - - const componentsBdcp = { - body: { - cell: EditableCellBdcp, - }, - }; - - - //Effect - useEffect(() => { - if (saveData) { - } - }, [saveData]); - - useEffect(() => { - // const { errcode, result } = fetchJSON('http://127.0.0.1:4523/m1/2602949-1933890-default/Service_BaseInfoWeb/travel_agency_products'); - console.log("get请求") - }, []) - - useEffect(() => { - if (selectedNodeKey) { - handleSaveForm(); - } - }, [selectedNodeKey]); - - - return ( -
- -
- - - - - - - - 供应商 }, - { title: 综费 }, - { title: '文章列表' } - ]} /> - } - > -

产品项目

- - {cpxm.map((item, index) => ( -
- - - - - ))} - - - - {tags.map(tag => ( - handleTagClick(tag)} - color={tag === selectedTag ? 'blue' : undefined} - style={{ cursor: 'pointer' }} - > - {tag} - - ))} - + - - }> - - - - - - handleChange('title', e.target.value)} - /> - - - handleChange('description', e.target.value)} - /> - - - - - -

供应商报价

- -
- {children} - - - {editing ? ( - - {inputNode} - - ) : ( - children - )} - - {editing ? ( - - {inputNode} - - ) : ( - children - )} -
- - - - - -

绑定产品

- -
- - - - - - - - - - - {datePickerVisible && ( - setDatePickerVisible(false)} - onCancel={() => setDatePickerVisible(false)} - > - - - )} - - ); -} -export default Index; - - diff --git a/src/views/gysgl/components/date.jsx b/src/views/gysgl/components/date.jsx deleted file mode 100644 index 51bc9ca..0000000 --- a/src/views/gysgl/components/date.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useState } from 'react'; -import { DatePicker, Input, Button } from 'antd'; -import dayjs from 'dayjs'; - -const DateComponent = ({ onDateChange }) => { - const dateFormat = 'YYYY/MM/DD'; - const { RangePicker } = DatePicker; - - const [selectedMonths, setSelectedMonths] = useState([]); - const [selectedDays, setSelectedDays] = useState([]); - - const handleChange = (date, dateString) => { - const dateFw = dateString[0] + "-" + dateString[1]; - onDateChange(dateFw); - }; - - const months = [ - 'January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', 'November', 'December' - ]; - - const days = [ - 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' - ]; - - const handleMonthClick = (month) => { - setSelectedMonths((prevSelectedMonths) => { - if (prevSelectedMonths.includes(month)) { - return prevSelectedMonths.filter((m) => m !== month); - } else { - return [...prevSelectedMonths, month]; - } - }); - }; - - const handleDayClick = (day) => { - setSelectedDays((prevSelectedDays) => { - if (prevSelectedDays.includes(day)) { - return prevSelectedDays.filter((d) => d !== day); - } else { - return [...prevSelectedDays, day]; - } - }); - }; - - return ( -
-

Title

- -

Data

- -

Months

-
- {months.map((month, index) => ( - - ))} -
-

Weekdays

-
- {days.map((day, index) => ( - - ))} -
-
- ); -}; - -export default DateComponent; diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 807e0e9..7188431 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,10 +1,603 @@ -import { createContext, useContext, useEffect, useState } from 'react'; -import { Table } from 'antd'; +import React, { useState, useEffect, useRef } from 'react'; +import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree } from 'antd'; +import { Link } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import DateComponent from '@/components/date'; +import { fetchJSON } from "@/utils/request"; -const Detail = ((props) => { + + +function Index() { + const { t } = useTranslation(); + const [form] = Form.useForm(); + const [editingKey, setEditingKey] = useState(''); + const [tags, setTags] = useState(['中文', 'English']); + const [isModalVisible, setIsModalVisible] = useState(false); + const [selectedTag, setSelectedTag] = useState('中文'); + const [saveData, setSaveData] = useState(null); + const [datePickerVisible, setDatePickerVisible] = useState(false); + const [currentKey, setCurrentKey] = useState(null); + const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); + const [formState, setFormState] = useState({}); + const [selectedNodeKey, setSelectedNodeKey] = useState(null); + const [selectedDateData, setSelectedDateData] = useState({ dateRange: null, selectedDays: [] }); + + const productProject = [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "remarks", name: t('products:Remarks') }, + { code: "open_hours", name: t('products:tourTime') }, + { code: "recommends_rate", name: t('products:recommendationRate') } + ]; + + const initialData = [ + { key: '1', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, + { key: '2', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, + { key: '3', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, + { key: '4', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, + ]; + const [quotation, setQuotation] = useState(initialData); + const isEditing = (record) => record.key === editingKey; + + const edit = (record) => { + form.setFieldsValue({ ...record }); + setEditingKey(record.key); + }; + + const cancel = () => { + setEditingKey(''); + }; + + const handleSave = async (key) => { + try { + const { info, ...restRow } = await form.validateFields(); + const newData = [...quotation]; + const index = newData.findIndex((item) => key === item.key); + if (index > -1) { + const item = newData[index]; + newData.splice(index, 1, { ...item, ...restRow }); + delete newData[index].quotation + delete newData[index].extras + setQuotation(newData); + setEditingKey(''); + } else { + newData.push(restRow); + setQuotation(newData); + setEditingKey(''); + } + } catch (errInfo) { + console.log('Validate Failed:', errInfo); + } + }; + + const handleDelete = (key) => { + const newData = [...quotation]; + const index = newData.findIndex((item) => key === item.key); + newData.splice(index, 1); + setQuotation(newData); + }; + + const handleAdd = () => { + const newData = { + key: `${quotation.length + 1}`, + value: '', + currency: '', + age_type: '', + weekdays: '', + group_size: '', + validityPeriod: '', + }; + setQuotation([...quotation, newData]); + }; + + const handleDateSelect = (key) => { + setCurrentKey(key); + setDatePickerVisible(true); + }; + + const handleDateChange = ({ dateRange, selectedDays }) => { + console.log('Date Range:', dateRange); + console.log('Selected Days:', selectedDays); + setSelectedDateData({ dateRange, selectedDays }) + }; + + const handleDateOk = () => { + const { dateRange } = selectedDateData; + + if (currentKey !== null) { + const newData = [...quotation]; + const index = newData.findIndex((item) => currentKey === item.key); + if (index > -1) { + newData[index].validityPeriod = dateRange; + + console.log() + setQuotation(newData); + setCurrentKey(null); + } + } + setDatePickerVisible(false); + } + + const EditableCell = ({ editing, dataIndex, title, inputType, record, children, handleDateSelect, ...restProps }) => { + let inputNode = inputType === 'number' ? : ; + if (dataIndex === 'validityPeriod' && editing) { + return ( + + ); + } + + return ( + + ); + }; + + const columns = [ + { title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '10%', editable: true }, + { title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '10%', editable: true }, + { title: t('products:currency'), dataIndex: 'currency', width: '10%', editable: true }, + { title: t('products:Types'), dataIndex: 'age_type', width: '10%', editable: true }, + { title: t('products:number'), dataIndex: 'group_size', width: '10%', editable: true }, + { title: t('products:validityPeriod'), dataIndex: 'validityPeriod', width: '30%', editable: true }, + { + title: t('products:operation'), + dataIndex: 'operation', + render: (_, record) => { + const editable = isEditing(record); + return editable ? ( + + handleSave(record.key)} style={{ marginRight: 8 }}>{t('products:save')} + {t('products:cancel')} + + ) : ( + + edit(record)} style={{ marginRight: 8 }}>{t('products:edit')} + handleDelete(record.key)}> + {t('products:delete')} + + + ); + }, + }, + ]; + + + const mergedColumns = columns.map((col) => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: (record) => ({ + record, + inputType: col.dataIndex === 'age' ? 'number' : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditing(record), + handleDateSelect: handleDateSelect, + }), + }; + }); + + const handleTagClick = (tag) => { + setSelectedTag(tag); + + }; + + const showModal = () => setIsModalVisible(true); + + const handleOk = () => setIsModalVisible(false); + + const handleCancel = () => setIsModalVisible(false); + + + const handleTagChange = (value) => { + if (!tags.includes(value)) { + setTags([...tags, value]); + setLanguageStatus([...languageStatus, { [value]: { title: "", description: "" } }]); + } + setSelectedTag(value); + setIsModalVisible(false); + }; + + const handleChange = (field, value) => { + const updatedLanguageStatus = languageStatus.map(lang => { + if (lang[selectedTag]) { + return { ...lang, [selectedTag]: { ...lang[selectedTag], [field]: value } }; + } + return lang; + }); + setLanguageStatus(updatedLanguageStatus); + }; + + const findLanguageDetails = (tag) => { + const lang = languageStatus.find(lang => lang[tag]); + return lang ? lang[tag] : { title: "", description: "" }; + }; + + + //树组件方法 + const handleNodeSelect = async (_, { node }) => { + + // 如果点击的是同一个节点,不做任何操作 + if (selectedNodeKey === node.key) return; + + // 保存当前表单数据 + if (selectedNodeKey) { + await handleSaveForm(); + } + + // 更新选中的节点 + setSelectedNodeKey(node.key); + + // 加载新节点的表单数据 + if (formState[node.key]) { + form.setFieldsValue(formState[node.key]); + setQuotation(formState[node.key].quotation || []); + setLanguageStatus(formState[node.key].lgc_details || languageStatus); + } else { + form.resetFields(); + setQuotation(initialData); + setLanguageStatus([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); + } + }; + + const handleSaveForm = async () => { + try { + const values = await form.validateFields(); + const newFormState = { + ...formState, + [selectedNodeKey]: { + ...values, + quotation, + lgc_details: languageStatus, + }, + }; + setFormState(newFormState); + } catch (error) { + console.error('Validation failed:', error); + } + }; + + + const onSave = (values) => { + const tempData = values; + tempData['quotation'] = quotation; + tempData['extras'] = bindingData; + tempData['lgc_details'] = languageStatus; + setSaveData(tempData); + console.log("保存的数据", tempData) + }; + + + //绑定产品 + const initBindingData = [ + { key: '1', title: '15', value: "美元", age_type: 'aa' }, + { key: '2', title: '15', value: "美元", age_type: 'aa' }, + { key: '3', title: '15', value: "美元", age_type: 'aa' }, + { key: '4', title: '15', value: "美元", age_type: 'aa' }, + ] + const [bindingData, setBindingData] = useState(initBindingData); + const isEditingBinding = (record) => record.key === editingKeyBinding; + const [editingKeyBinding, setEditingKeyBinding] = useState(''); + + const editBinding = (record) => { + form.setFieldsValue({ ...record }); + setEditingKeyBinding(record.key); + }; + const cancelBinding = () => { + setEditingKeyBinding(''); + }; + const EditableCellBinding = ({ + editing, + dataIndex, + title, + inputType, + record, + index, + children, + ...restProps + }) => { + const inputNode = inputType === 'number' ? : ; return ( - <> - + ); -}); -export default Detail; + }; + const bindingColums = [ + { title: t('products:Name'), dataIndex: 'title', width: '15%', editable: true }, + { title: t('products:price'), dataIndex: 'value', width: '15%', editable: true }, + { title: t('products:Types'), dataIndex: 'age_type', width: '40%', editable: true }, + { + title: t('products:operation'), + dataIndex: 'operation', + render: (_, record) => { + const editable = isEditingBinding(record); + return editable ? ( + + handleSaveBinding(record.key)} style={{ marginRight: 8 }}>{t('products:save')} + {t('products:cancel')} + + ) : ( + + editBinding(record)} style={{ marginRight: 8 }}>{t('products:edit')} + handleDeleteBinding(record.key)}> + {t('products:delete')} + + + ); + }, + }, + ].map(col => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: record => ({ + record, + inputType: col.dataIndex === 'value' ? 'number' : 'text', + dataIndex: col.dataIndex, + title: col.title, + editing: isEditingBinding(record), + }), + }; + }); + + + + const handleAddBinding = () => { + const newData = { + key: `${bindingData.length + 1}`, + title: '', + value: '', + age_type: '', + }; + setBindingData([...bindingData, newData]); + setEditingKeyBinding(''); // 添加这一行 + }; + + const handleSaveBinding = async (key) => { + try { + const row = await form.validateFields(); + const newData = [...bindingData]; + const { value, title, age_type } = row + const index = newData.findIndex((item) => key === item.key); + if (index > -1) { + const item = newData[index]; + newData.splice(index, 1, { ...item, value, title, age_type }); + + setBindingData(newData); + setEditingKeyBinding(''); + } else { + newData.push(row); + setBindingData(newData); + setEditingKeyBinding(''); + } + } catch (errInfo) { + console.log('Validate Failed:', errInfo); + } + }; + + + const handleDeleteBinding = (key) => { + const newData = [...bindingData]; + const index = newData.findIndex((item) => key === item.key); + newData.splice(index, 1); + setBindingData(newData); + }; + + const componentsBinding = { + body: { + cell: EditableCellBinding, + }, + }; + + const treeData = [ + { + title: '综费', + key: 'zf', + selectable: false, + children: [{ title: '北京怡然假日', key: 'bjyrjr' }] + }, + { + title: '车费', + key: 'cf', + selectable: false, + children: [ + { title: '北京', key: 'bj' }, + { title: '天津', key: 'tj' }, + { title: '北京-天津', key: 'bj-tj-3-5' } + ] + } + ] + + + + //Effect + useEffect(() => { + if (saveData) { + + } + }, [saveData]); + + useEffect(() => { + // const { errcode, result } = fetchJSON('http://127.0.0.1:4523/m1/2602949-1933890-default/Service_BaseInfoWeb/travel_agency_products'); + console.log("get请求") + + }, []) + + useEffect(() => { + if (selectedNodeKey) { + handleSaveForm(); + } + }, [selectedNodeKey]); + + + return ( +
+ +
+ + + + + + + + 供应商 }, + { title: 综费 }, + { title: '文章列表' } + ]} /> + } + > +

{t('products:productProject')}

+ + {productProject.map((item, index) => ( +
+ + + + + ))} + + + + {tags.map(tag => ( + handleTagClick(tag)} + color={tag === selectedTag ? 'blue' : undefined} + style={{ cursor: 'pointer' }} + > + {tag} + + ))} + + + + }> + + + + + + handleChange('title', e.target.value)} + /> + + + handleChange('description', e.target.value)} + /> + + + + + +

{t('products:supplierQuotation')}

+ +
+ {children} + + + {editing ? ( + + {inputNode} + + ) : ( + children + )} + + {editing ? ( + + {inputNode} + + ) : ( + children + )} +
+ + + + + +

{t('products:bindingProducts')}

+ +
+ + + + + + + + + + + {datePickerVisible && ( + setDatePickerVisible(false)} + > + + + )} + + ); +} +export default Index; + + From 3c533c96b41316a6e1f6a6ab9e0bb3de33bbb188 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 25 Jun 2024 13:46:31 +0800 Subject: [PATCH 21/46] 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 22/46] =?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 23/46] =?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 24/46] =?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 25/46] =?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 26/46] =?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 27/46] =?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() { Date: Wed, 26 Jun 2024 11:40:31 +0800 Subject: [PATCH 28/46] =?UTF-8?q?1.=E5=AE=8C=E5=96=84=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=95=88=E6=9E=9C=202.=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E8=A1=A8=E5=8D=95=E5=AF=B9=E5=BA=94=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 2 +- public/locales/zh/products.json | 4 +- src/views/products/Detail.jsx | 281 +++++++++++++++++++++++--------- 3 files changed, 203 insertions(+), 84 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 0ccfecf..b0363b7 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -56,7 +56,7 @@ "validityPeriod":"Validity period", "operation": "Operation", "price": "Price", - + "weekends":"Weekends", "save":"save", "edit":"edit", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 3abea44..17ec532 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -52,9 +52,7 @@ "Child": "儿童" }, - "#": "#" - "Auditors": "审核人员", - "AuditDate": "审核时间", + "#": "#", "productProject": "产品项目", "Code": "代码", diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 7188431..8ce7648 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -4,23 +4,25 @@ import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import DateComponent from '@/components/date'; import { fetchJSON } from "@/utils/request"; - - +import { type } from 'windicss/utils'; +import { searchAgencyAction, getAgencyProductsAction } from '@/stores/Products/Index'; function Index() { const { t } = useTranslation(); const [form] = Form.useForm(); - const [editingKey, setEditingKey] = useState(''); + const [editingid, setEditingid] = useState(''); const [tags, setTags] = useState(['中文', 'English']); const [isModalVisible, setIsModalVisible] = useState(false); const [selectedTag, setSelectedTag] = useState('中文'); const [saveData, setSaveData] = useState(null); const [datePickerVisible, setDatePickerVisible] = useState(false); - const [currentKey, setCurrentKey] = useState(null); + const [currentid, setCurrentid] = useState(null); const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); const [formState, setFormState] = useState({}); - const [selectedNodeKey, setSelectedNodeKey] = useState(null); + const [selectedNodeid, setSelectedNodeid] = useState(null); const [selectedDateData, setSelectedDateData] = useState({ dateRange: null, selectedDays: [] }); + const [startValue,setStartValue] = useState(null); + const [endValue,setEndValue] = useState(null); const productProject = [ { code: "code", name: t('products:Code') }, @@ -31,88 +33,109 @@ function Index() { ]; const initialData = [ - { key: '1', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, - { key: '2', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, - { key: '3', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, - { key: '4', adult_cost: '15', child_cost: "美元", currency: 'aa', age_type: 'as', group_size: 'dawd', validityPeriod: 'dawda' }, + { id: '1', adult_cost: '1000', child_cost: "800", currency: 'RMB', age_type: '每人', group_size_min: 4, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, + { id: '2', adult_cost: '1200', child_cost: "900", currency: 'RMB', age_type: '每人', group_size_min: 3, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, + { id: '3', adult_cost: '1500', child_cost: "1200", currency: 'RMB', age_type: '每人', group_size_min: 2, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, + { id: '4', adult_cost: '1100', child_cost: "700", currency: 'RMB', age_type: '每人', group_size_min: 1, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, ]; const [quotation, setQuotation] = useState(initialData); - const isEditing = (record) => record.key === editingKey; + const isEditing = (record) => record.id === editingid; const edit = (record) => { form.setFieldsValue({ ...record }); - setEditingKey(record.key); + setEditingid(record.id); }; const cancel = () => { - setEditingKey(''); + setEditingid(''); }; - const handleSave = async (key) => { + const handleSave = async (id) => { try { const { info, ...restRow } = await form.validateFields(); const newData = [...quotation]; - const index = newData.findIndex((item) => key === item.key); + const index = newData.findIndex((item) => id === item.id); if (index > -1) { const item = newData[index]; newData.splice(index, 1, { ...item, ...restRow }); delete newData[index].quotation delete newData[index].extras setQuotation(newData); - setEditingKey(''); + setEditingid(''); } else { newData.push(restRow); setQuotation(newData); - setEditingKey(''); + setEditingid(''); } } catch (errInfo) { console.log('Validate Failed:', errInfo); } }; - const handleDelete = (key) => { + const handleDelete = (id) => { const newData = [...quotation]; - const index = newData.findIndex((item) => key === item.key); + const index = newData.findIndex((item) => id === item.id); newData.splice(index, 1); setQuotation(newData); }; const handleAdd = () => { const newData = { - key: `${quotation.length + 1}`, + id: `${quotation.length + 1}`, value: '', currency: '', age_type: '', weekdays: '', - group_size: '', - validityPeriod: '', + use_dates_start: '', + use_dates_end: '', + group_size_min:'', + group_size_max:'' }; setQuotation([...quotation, newData]); }; - const handleDateSelect = (key) => { - setCurrentKey(key); + const handleDateSelect = (id) => { + setCurrentid(id); setDatePickerVisible(true); }; const handleDateChange = ({ dateRange, selectedDays }) => { - console.log('Date Range:', dateRange); - console.log('Selected Days:', selectedDays); + + // 计算周末 + const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday']; + let weekDayCount = selectedDays.map(day => weekdays.indexOf(day) + 1).sort().join(','); + console.log('Weekday Count:', weekDayCount); + if (!weekDayCount || weekDayCount.length === 0) { + weekDayCount = "全年"; + } + const newData = [...quotation]; + const index = newData.findIndex((item) => currentid === item.id); + if (index > -1) { + newData[index].weekdays = weekDayCount; + setQuotation(newData); + } + setSelectedDateData({ dateRange, selectedDays }) }; const handleDateOk = () => { const { dateRange } = selectedDateData; + const dateRangeList = dateRange.split('-'); + console.log("dateRangeList",dateRangeList); + const use_dates_start = dateRangeList[0]; + const use_dates_end = dateRangeList[1]; - if (currentKey !== null) { + console.log("dateRange",dateRange) + + if (currentid !== null) { const newData = [...quotation]; - const index = newData.findIndex((item) => currentKey === item.key); + console.log("newData",newData) + const index = newData.findIndex((item) => currentid === item.id); if (index > -1) { - newData[index].validityPeriod = dateRange; - - console.log() + newData[index].use_dates_start = use_dates_start; + newData[index].use_dates_end = use_dates_end; setQuotation(newData); - setCurrentKey(null); + setCurrentid(null); } } setDatePickerVisible(false); @@ -124,11 +147,50 @@ function Index() { return ( ); } + if (dataIndex === 'age_type' && editing) { + inputNode = ( + + ); + } + + if (dataIndex === 'currency' && editing) { + inputNode = ( + + ); + } + + if (dataIndex === 'group_size' && editing) { + const groupSizeValue = `${record.group_size_min}-${record.group_size_max}`; + return ( + + ); + } + return ( + @@ -498,7 +616,7 @@ function Index() {
{tags.map(tag => ( handleTagClick(tag)} color={tag === selectedTag ? 'blue' : undefined} style={{ cursor: 'pointer' }} @@ -581,6 +699,9 @@ function Index() { + From a3d02f4caf28999337aa4c430046b65374e9efda Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Wed, 26 Jun 2024 11:58:18 +0800 Subject: [PATCH 29/46] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 2 ++ src/views/App.jsx | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index 6e84eb9..eb70c92 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -46,11 +46,13 @@ import './i18n'; const { loginToken, userId } = usingStorage() const initAppliction = async () => { + if (isNotEmpty(loginToken)) { appendRequestParams('token', loginToken) } if (isNotEmpty(userId)) { + appendRequestParams('wu_id', userId) await fireAuth() } } diff --git a/src/views/App.jsx b/src/views/App.jsx index f9a37ad..0ebcff4 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -32,7 +32,7 @@ function App() { const [validateUserPassword, tokenTimeout, isPermitted] = useAuthStore( (state) => [state.validateUserPassword, state.tokenTimeout, state.isPermitted]) - const { loginToken, userDetail, userId } = usingStorage() + const { loginToken } = usingStorage() const noticeUnRead = useNoticeStore((state) => state.noticeUnRead) const href = useHref() @@ -42,10 +42,6 @@ function App() { // 除了路由 /p...以外都需要登陆系统 const needToLogin = href !== '/login' && isEmpty(loginToken) - if (!needToLogin) { - appendRequestParams('token', loginToken) - appendRequestParams('wu_id', userId) - } useEffect(() => { fetchUserDetail(loginToken) .then(u => { From 1084db4b96f0eb50193aee8102576d271521cb83 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 12:06:10 +0800 Subject: [PATCH 30/46] =?UTF-8?q?=E6=90=9C=E7=B4=A2=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SearchForm.jsx | 4 ++-- src/views/account/Management.jsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index f6bad2c..9f985b9 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -209,7 +209,7 @@ function getFields(props) { ), item( 'username', - 3, + 99, , @@ -217,7 +217,7 @@ function getFields(props) { ), item( 'realname', - 4, + 99, , diff --git a/src/views/account/Management.jsx b/src/views/account/Management.jsx index 6c2c323..737ff5a 100644 --- a/src/views/account/Management.jsx +++ b/src/views/account/Management.jsx @@ -243,6 +243,7 @@ function Management() { fieldProps: { dates: { label: t('group:ArrivalDate') }, }, + sort: { username: 1, realname: 2, dates: 3}, }} onSubmit={(err, formValues, filedsVal) => { console.info(formValues) From d0a4453ce6d6a5ff2f7d3807a75b71c342376f95 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 14:02:07 +0800 Subject: [PATCH 31/46] =?UTF-8?q?=E4=BA=A7=E5=93=81=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=9A=84=20i8n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 5 +++-- public/locales/en/products.json | 7 +++++++ public/locales/zh/common.json | 15 ++++++++------- public/locales/zh/products.json | 11 +++++++++-- src/components/SearchForm.jsx | 2 +- src/hooks/useDatePresets.js | 22 ++++++++++++++++++++-- src/views/products/Detail/Extras.jsx | 14 +++++++------- 7 files changed, 55 insertions(+), 21 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 8ed1854..c87b7cd 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -26,8 +26,9 @@ "Export": "Export", "Copy": "Copy", - "sureCancel": "Sure you want to cancel?", - "sureDelete":"Sure you want to delete?", + "sureCancel": "Are you sure to cancel?", + "sureDelete":"Are you sure to delete?", + "Yes": "Yes", "Success": "Success", "Failed": "Failed", diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 4997e31..79bb594 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -39,6 +39,13 @@ "CreateDate": "Create Date", "AuditedBy": "Audited By", "AuditDate": "Audit Date", + "OpenHours": "Open Hours", + "Duration": "Duration", + "KM": "KM", + "RecommendsRate": "RecommendsRate", + "OpenWeekdays": "Open Weekdays", + "DisplayToC": "DisplayToC", + "Dept": "Dept", "productProject": "Product project", "Code": "Code", diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index 82b43f9..459ff35 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -28,6 +28,7 @@ "sureCancel": "确定取消?", "sureDelete":"确定删除?", + "Yes": "是", "Success": "成功", "Failed": "失败", @@ -62,13 +63,13 @@ "thisYear": "今年" }, "weekdays": { - "1": "一", - "2": "二", - "3": "三", - "4": "四", - "5": "五", - "6": "六", - "7": "日" + "1": "周一", + "2": "周二", + "3": "周三", + "4": "周四", + "5": "周五", + "6": "周六", + "7": "周日" }, "weekdaysShort": { "1": "一", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 55005ce..65cb019 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -37,11 +37,18 @@ "AuState": "审核状态", "CreatedBy": "提交人员", "CreateDate": "提交时间", - - "AuditedBy": "审核人员", "AuditDate": "审核时间", + "OpenHours": "游览时间", + "Duration": "游览时长", + "KM": "公里数", + "RecommendsRate": "推荐指数", + "OpenWeekdays": "周开放日", + "DisplayToC": "报价信显示", + "Dept": "小组", + + "productProject": "产品项目", "Code": "代码", "City": "城市", diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 9f985b9..f896792 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -3,7 +3,7 @@ import { Form, Input, Row, Col, Select, DatePicker, Space, Button } from 'antd'; import { objectMapper, at } from '@/utils/commons'; import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config'; import useFormStore from '@/stores/Form'; -import useDatePresets from '@/hooks/useDatePresets'; +import {useDatePresets} from '@/hooks/useDatePresets'; import { useTranslation } from 'react-i18next'; import { fetchJSON } from '@/utils/request'; diff --git a/src/hooks/useDatePresets.js b/src/hooks/useDatePresets.js index 87397b9..2eb4c7d 100644 --- a/src/hooks/useDatePresets.js +++ b/src/hooks/useDatePresets.js @@ -1,8 +1,9 @@ import { useEffect, useState } from 'react'; import dayjs from "dayjs"; import { useTranslation } from 'react-i18next'; +import i18n from '@/i18n'; -const useDatePresets = () => { +export const useDatePresets = () => { const [presets, setPresets] = useState([]); const { t, i18n } = useTranslation(); @@ -39,4 +40,21 @@ const useDatePresets = () => { return presets; } -export default useDatePresets; +export const useWeekdays = () => { + const [data, setData] = useState([]); + const { t, i18n } = useTranslation(); + useEffect(() => { + const newData = [ + { value: '1', label: t('weekdays.1') }, + { value: '2', label: t('weekdays.2') }, + { value: '3', label: t('weekdays.3') }, + { value: '4', label: t('weekdays.4') }, + { value: '5', label: t('weekdays.5') }, + { value: '6', label: t('weekdays.6') }, + { value: '7', label: t('weekdays.7') }, + ]; + setData(newData); + return () => {}; + }, [i18n.language]); + return data; +}; diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index b5e0f1b..4caf981 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -1,9 +1,9 @@ -import { createContext, useContext, useEffect, useState } from 'react'; +import { 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, { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index'; -import { isEmpty, cloneDeep } from '@/utils/commons'; +import { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index'; +import { cloneDeep } from '@/utils/commons'; import SearchForm from '@/components/SearchForm'; import RequireAuth from '@/components/RequireAuth'; @@ -63,7 +63,7 @@ const NewAddonModal = ({ onPick, ...props }) => { return ( <> setOpen(false)} destroyOnClose> @@ -119,14 +119,14 @@ const Extras = ({ productId, onChange, ...props }) => { 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')); + newSuccess ? message.success(t('Action')+t('Success')) : message.error(t('Action')+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')); + delSuccess ? message.success(t('Action')+t('Success')) : message.error(t('Action')+t('Failed')); await handleGetAgencyProductExtras(); }; @@ -151,7 +151,7 @@ const Extras = ({ productId, onChange, ...props }) => { dataIndex: 'operation', width: '4rem', render: (_, r) => ( - handleDelAddon(r)} > + handleDelAddon(r)} okText={t('Yes')} > From 461263839bbf215a425fdf78b4ba1205580fa4ab Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 14:50:51 +0800 Subject: [PATCH 32/46] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86:=20?= =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E9=A6=96=E9=A1=B5:=20=E6=A0=B8=E5=AF=B9?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E7=BB=93=E6=9E=9C=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Manage.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/products/Manage.jsx b/src/views/products/Manage.jsx index 3e5d3b6..65cfc6b 100644 --- a/src/views/products/Manage.jsx +++ b/src/views/products/Manage.jsx @@ -49,10 +49,10 @@ function Index() { const columns = [ { title: t('products:Vendor'), key: 'vendor', dataIndex: 'travel_agency_name' }, - { title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by' }, + { title: t('products:CreatedBy'), key: 'created_by', dataIndex: 'created_by_name' }, { title: t('products:CreateDate'), key: 'create_date', dataIndex: 'create_date' }, { title: t('products:AuState'), key: 'audit_state', dataIndex: 'audit_state' }, - { title: t('products:AuditedBy'), key: 'audited_by', dataIndex: 'audited_by' }, + { title: t('products:AuditedBy'), key: 'audited_by', dataIndex: 'audited_by_name' }, { title: t('products:AuditDate'), key: 'audit_date', dataIndex: 'audit_date' }, { title: '', From bebb8c0a093c0fec50805352ff15c160bd2a975b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 14:55:06 +0800 Subject: [PATCH 33/46] =?UTF-8?q?feat:=20=E4=BB=8E=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E8=8E=B7=E5=8F=96=E9=BB=98=E8=AE=A4=E7=9A=84?= =?UTF-8?q?HT=E8=AF=AD=E7=A7=8D=E7=BC=96=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/i18n/LanguageSwitcher.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/i18n/LanguageSwitcher.jsx b/src/i18n/LanguageSwitcher.jsx index dd1a743..f09a07e 100644 --- a/src/i18n/LanguageSwitcher.jsx +++ b/src/i18n/LanguageSwitcher.jsx @@ -8,16 +8,15 @@ const i18n_to_htcode = { 'en': 1, }; -export const useLanguage = () => { +export const useDefaultLgc = () => { const { i18n } = useTranslation(); - return { language: i18n.language, }; + return { language: i18n_to_htcode[i18n.language], }; }; - /** * 语言选择组件 */ const Language = () => { - const { t, i18n } = useTranslation(); +const { t, i18n } = useTranslation(); const [selectedKeys, setSelectedKeys] = useState([i18n.language]); useEffect(() => { From 4b77aa689e8a7c9396533fdaf3ffade85365ffc0 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 15:57:36 +0800 Subject: [PATCH 34/46] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=BC=BA=E5=B0=91?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E5=90=8D=E7=A7=B0=E6=97=B6,=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E8=AF=AD=E7=A7=8D=E8=A1=A8=E4=B8=AD=E6=96=87=E5=90=8D?= =?UTF-8?q?=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Audit.jsx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 72a5742..2cbb8b1 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -62,6 +62,8 @@ const PriceTable = ({ dataSource, refresh }) => { const { message, notification } = App.useApp(); const stateMapVal = useProductsAuditStatesMapVal(); + // console.log(dataSource); + const handleAuditPriceItem = (state, row) => { postProductsQuoteAuditAction(state, { id: row.id, travel_agency_id: activeAgency.travel_agency_id }) .then((json) => { @@ -83,7 +85,7 @@ const PriceTable = ({ dataSource, refresh }) => { }; const columns = [ - { key: 'title', dataIndex: ['info', 'title'], width: '16rem', 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 }), render: (text, r) => text || r.lgc_details?.['2']?.title || '' }, { 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'), }, @@ -135,16 +137,33 @@ const TypesPanels = (props) => { const [activeKey, setActiveKey] = useState([]); const [showTypes, setShowTypes] = useState([]); useEffect(() => { - // 只显示有产品的类型; 展开产品的价格表, 合并名称列 + // 只显示有产品的类型; 展开产品的价格表, 合并名称列; 转化为价格主表, 携带产品属性信息 const hasDataTypes = Object.keys(agencyProducts); const _show = productsTypes .filter((kk) => hasDataTypes.includes(kk.value)) .map((ele) => ({ ...ele, + extra: t('Table.Total', { total: agencyProducts[ele.value].length }), children: ( 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 }))), [])} + 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, + lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}), + rowSpan: i === 0 ? c.quotation.length : 0, + })) + ), + [] + )} refresh={props.refresh} /> ), From 1534ce6979668541dc269a967341793597c1515d Mon Sep 17 00:00:00 2001 From: Lei OT Date: Wed, 26 Jun 2024 17:02:18 +0800 Subject: [PATCH 35/46] =?UTF-8?q?debug:=20=E6=90=9C=E7=B4=A2=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E7=BB=91=E5=AE=9A=E7=9A=84=E9=99=84=E5=8A=A0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE;=20=E4=BB=B7=E6=A0=BCvalue=E6=94=B9=E4=B8=BAadult=5Fc?= =?UTF-8?q?ost;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 1 + public/locales/zh/products.json | 3 ++- src/hooks/useProductsSets.js | 33 ++++++++++++++++++---------- src/views/products/Audit.jsx | 4 ++-- src/views/products/Detail/Extras.jsx | 15 ++++++------- 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 6a62774..2a07ae5 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -1,4 +1,5 @@ { + "ProductType": "ProductType", "type": { "Experience": "Experience", "Car": "Transport Services", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 65cb019..1e1083b 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -1,4 +1,5 @@ { + "ProductType": "项目类型", "type": { "Experience": "综费", "Car": "车费", @@ -78,7 +79,7 @@ "GroupSize": "人等", "UseDates": "使用日期", - "Weekdays": "有效日/周X", + "Weekdays": "周末", "OnWeekdays": "周: ", "Unlimited": "不限", diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 0dd63f7..5ee66d3 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -1,5 +1,7 @@ import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import useAuthStore from '@/stores/Auth'; +import { PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/config'; /** * 产品管理 相关的预设数据 @@ -89,17 +91,26 @@ export const useProductsAuditStatesMapVal = (value) => { /** * @ignore */ -export const useProductsTypesFieldsets = (type, role) => { - const infoDefault = ['code', 'title']; +export const useProductsTypesFieldsets = (type) => { + const [isPermitted] = useAuthStore((state) => [state.isPermitted]); + const infoDefault = [['code'], ['title']]; const infoAdmin = ['remarks', 'dept', 'display_to_c']; const infoTypesMap = { - '6': [], - 'B': ['city_id', 'km'], - 'J': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c'], - 'Q': ['description', 'city_id', 'duration', ], - 'D': ['description', 'city_id', 'recommends_rate','duration',], - '7': ['description', 'city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], // todo: 怎么是2个图 - 'C': ['description', 'city_id',], - '8': [], // todo: ? + '6': [[],[]], + 'B': [['city_id', 'km'], []], + 'J': [['city_id', 'recommends_rate', 'duration', 'display_to_c'], ['description',]], + 'Q': [['city_id', 'duration', ], ['description',]], + 'D': [['city_id', 'recommends_rate','duration',], ['description',]], + '7': [['city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], ['description',]], // todo: 怎么是2个图 + 'C': [['city_id',], ['description',]], + '8': [[],[]], // todo: ? }; -}; + const thisTypeFieldset = (_type) => { + const adminSet = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? infoAdmin : []; + return [ + [...infoDefault[0], ...infoTypesMap[_type][0], ...adminSet], + [...infoDefault[1], ...infoTypesMap[_type][1]] + ]; + }; + return thisTypeFieldset(type); +} diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 2cbb8b1..ca38cfd 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -86,8 +86,8 @@ const PriceTable = ({ dataSource, refresh }) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || '' }, - { 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: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_name }) => `${adult_cost} ${currency} / ${unit_name}` }, + { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_name }) => `${child_cost} ${currency} / ${unit_name}` }, // {key: 'price', title: t('Currency'), }, // {key: 'currency', title: t('Currency'), }, // {key: 'unit', title: t('Unit'), }, diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index 4caf981..c22a2fb 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -24,27 +24,26 @@ const NewAddonModal = ({ onPick, ...props }) => { const { starttime, endtime, ...param } = copyObject; setSearchLoading(true); setSearchResult([]); - const result = await getAgencyProductsAction({ ...param, audit_state: '1', travel_agency_id, use_year }); + // debug: audit_state: '1', + 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: + // todo: 如何显示价格表 const searchResultColumns = [ + { key: 'ptype', dataIndex: ['info', 'product_type_name'], width: '6rem', title: t('products:ProductType') }, { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('products:Title') }, { title: t('products:price'), - dataIndex: ['quotation', '0', 'value'], + dataIndex: ['quotation', '0', 'adult_cost'], width: '10rem', - render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 + render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 }, { key: 'action', @@ -143,7 +142,7 @@ const Extras = ({ productId, onChange, ...props }) => { dataIndex: ['quotation', '0', 'value'], width: '10rem', - render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 + render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 }, // { title: t('products:Types'), dataIndex: 'age_type', width: '40%', }, { From cc2bf23bf5d4c0344afa6a3b765e1d158356bd7c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 27 Jun 2024 11:14:52 +0800 Subject: [PATCH 36/46] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20`=E9=A4=90`=20=E7=B1=BB=E5=9E=8B=E4=BD=BF=E7=94=A8?= =?UTF-8?q?`R`=20;=20=E4=BC=98=E5=8C=96=E5=AE=A1=E6=A0=B8=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E4=BB=B7=E6=A0=BC=E8=A1=A8;=20=E9=99=84=E5=8A=A0?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=A1=A8=E6=98=BE=E7=A4=BA=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?;=20=E8=B6=85=E5=85=AC=E9=87=8CUltra=20Service?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/products.json | 10 ++-- public/locales/zh/products.json | 10 ++-- src/hooks/useProductsSets.js | 78 +++++++++++++++------------- src/views/products/Audit.jsx | 8 ++- src/views/products/Detail/Extras.jsx | 5 +- 5 files changed, 62 insertions(+), 49 deletions(-) diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 2a07ae5..0e90423 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -1,5 +1,5 @@ { - "ProductType": "ProductType", + "ProductType": "Product Type", "type": { "Experience": "Experience", "Car": "Transport Services", @@ -8,8 +8,7 @@ "Attractions": "Attractions", "Meals": "Meals", "Extras": "Extras", - "Overtravel": "超公里", - "Special": "Special" + "UltraService": "Ultra Service" }, "EditComponents": { "info": "Product Information", @@ -30,6 +29,11 @@ "Rejected": "Reject", "Published": "Publish" }, + "PriceUnit": { + "0": "Person", + "1": "Group", + "title": "Price Unit" + }, "Status": "Status", "State": "State", diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 1e1083b..8c592fb 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -3,13 +3,12 @@ "type": { "Experience": "综费", "Car": "车费", - "Guide": "导服", + "Guide": "导游", "Package": "包价线路", "Attractions": "景点", "Meals": "餐费", "Extras": "附加项目", - "Overtravel": "超公里", - "Special": "特殊项目" + "UltraService": "超公里" }, "EditComponents": { "info": "产品信息", @@ -30,6 +29,11 @@ "Rejected": "审核拒绝", "Published": "审核发布" }, + "PriceUnit": { + "0": "每人", + "1": "每团", + "title": "报价单位" + }, "Status": "状态", "State": "状态", diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 5ee66d3..015fa8a 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -6,39 +6,39 @@ import { PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/confi /** * 产品管理 相关的预设数据 * 项目类型 -酒店预定 1 -火车 2 -飞机票务 3 -游船 4 -快巴 5 -旅行社(综费) 6 -景点 7 -特殊项目 8 -其他 9 -酒店 A -超公里 B -餐费 C -小包价 D -站 X -购物 S -餐 R -娱乐 E -精华线路 T -客人testimonial F -线路订单 O -省 P -信息 I -国家 G -城市 K -图片 H -地图 M -包价线路 L -节日节庆 V -火车站 N -手机租赁 Z - * * webht 类型, 20240624 新增HT类型 -Q 导游 -J 车费 + * * 酒店预定 1 + * * 火车 2 + * * 飞机票务 3 + * * 游船 4 + * * 快巴 5 + * * 旅行社(综费) 6 + * * 景点 7 + * * 特殊项目 8 + * * 其他 9 + * * 酒店 A + * * 超公里 B + * * 餐费 C + * * 小包价 D + * * 站 X + * * 购物 S + * * 餐 R (餐厅) + * * 娱乐 E + * * 精华线路 T + * * 客人testimonial F + * * 线路订单 O + * * 省 P + * * 信息 I + * * 国家 G + * * 城市 K + * * 图片 H + * * 地图 M + * * 包价线路 L (已废弃) + * * 节日节庆 V + * * 火车站 N + * * 手机租赁 Z + * * ---- webht 类型, 20240624 新增HT类型 ---- + * * 导游 Q + * * 车费 J */ export const useProductsTypes = () => { @@ -48,20 +48,24 @@ export const useProductsTypes = () => { useEffect(() => { const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, - { label: t('products:type.Overtravel'), value: 'B', key: 'B' }, + { label: t('products:type.UltraService'), 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.Attractions'), value: '7', key: '7' }, - { label: t('products:type.Meals'), value: 'C', key: 'C' }, + { label: t('products:type.Meals'), value: 'R', key: 'R' }, { label: t('products:type.Extras'), value: '8', key: '8' }, - // { label: t('products:type.Special'), value: 'Special', key: 'Special' }, ]; setTypes(newData); }, [i18n.language]); return types; }; +export const useProductsTypesMapVal = (value) => { + const stateSets = useProductsTypes(); + const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); + return stateMapVal; +}; export const useProductsAuditStates = () => { const [types, setTypes] = useState([]); @@ -102,7 +106,7 @@ export const useProductsTypesFieldsets = (type) => { 'Q': [['city_id', 'duration', ], ['description',]], 'D': [['city_id', 'recommends_rate','duration',], ['description',]], '7': [['city_id', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], ['description',]], // todo: 怎么是2个图 - 'C': [['city_id',], ['description',]], + 'R': [['city_id',], ['description',]], '8': [[],[]], // todo: ? }; const thisTypeFieldset = (_type) => { diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index ca38cfd..9a23e0d 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -85,11 +85,9 @@ const PriceTable = ({ dataSource, refresh }) => { }; const columns = [ - { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || '' }, - { key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_name }) => `${adult_cost} ${currency} / ${unit_name}` }, - { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_name }) => `${child_cost} ${currency} / ${unit_name}` }, - // {key: 'price', title: t('Currency'), }, - // {key: 'currency', title: t('Currency'), }, + { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '' }, + { key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_id, unit_name }) => `${adult_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, + { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_id, unit_name }) => `${child_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, // {key: 'unit', title: t('Unit'), }, { key: 'groupSize', diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index c22a2fb..2ee8587 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -8,12 +8,15 @@ import SearchForm from '@/components/SearchForm'; import RequireAuth from '@/components/RequireAuth'; import { PERM_PRODUCTS_MANAGEMENT } from '@/config'; +import { useProductsTypesMapVal } from '@/hooks/useProductsSets'; const NewAddonModal = ({ onPick, ...props }) => { const { travel_agency_id, use_year } = useParams(); const { t } = useTranslation(); const { notification, message } = App.useApp(); + const productsTypesMapVal = useProductsTypesMapVal(); + const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); // bind loading const [searchLoading, setSearchLoading] = useState(false); @@ -37,7 +40,7 @@ const NewAddonModal = ({ onPick, ...props }) => { // todo: 如何显示价格表 const searchResultColumns = [ - { key: 'ptype', dataIndex: ['info', 'product_type_name'], width: '6rem', title: t('products:ProductType') }, + { key: 'ptype', dataIndex: ['info', 'product_type_id'], width: '6rem', title: t('products:ProductType'), render: (text, r) => productsTypesMapVal[text].label }, { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('products:Title') }, { title: t('products:price'), From ff6071cd2fbf602d1fdcc9f078ce8eddb7cbc3c6 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 27 Jun 2024 11:20:02 +0800 Subject: [PATCH 37/46] =?UTF-8?q?wu=5Fid=20=E6=94=B9=20lmi=5Fsn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.jsx | 2 +- src/stores/Auth.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.jsx b/src/main.jsx index f7a0b42..02f2244 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -53,7 +53,7 @@ const initAppliction = async () => { } if (isNotEmpty(userId)) { - appendRequestParams('wu_id', userId) + appendRequestParams('lmi_sn', userId) await fireAuth() } } diff --git a/src/stores/Auth.js b/src/stores/Auth.js index 421d4cb..344e147 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -98,7 +98,7 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ setStorage(KEY_USER_ID, userDetail.LMI_SN) setStorage(KEY_TRAVEL_AGENCY_ID, userDetail.LMI_VEI_SN) appendRequestParams('token', loginToken) - appendRequestParams('wu_id', userDetail.LMI_SN) + appendRequestParams('lmi_sn', userDetail.LMI_SN) // loadPageSpy(`${json.Result.VName}-${json.Result.LoginName}`) startTokenInterval() }, @@ -172,4 +172,4 @@ const useAuthStore = create(obervseLifecycle((set, get) => ({ }))) -export default useAuthStore \ No newline at end of file +export default useAuthStore From 2ad323edbbba48902f4837a3971af366799bb247 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 27 Jun 2024 15:01:46 +0800 Subject: [PATCH 38/46] =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E5=AE=A1=E6=A0=B8?= =?UTF-8?q?=E4=BB=B7=E6=A0=BC:=20=E8=B6=85=E5=85=AC=E9=87=8C+=E5=85=AC?= =?UTF-8?q?=E9=87=8C=E6=95=B0=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Audit.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 9a23e0d..803507f 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -56,7 +56,7 @@ const Header = ({ title, agency, refresh, ...props }) => { ); }; -const PriceTable = ({ dataSource, refresh }) => { +const PriceTable = ({ productType, dataSource, refresh }) => { const { t } = useTranslation('products'); const [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]); const { message, notification } = App.useApp(); @@ -86,6 +86,7 @@ const PriceTable = ({ dataSource, refresh }) => { const columns = [ { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '' }, + ...(productType === 'B' ? [{ key: 'km', dataIndex: ['info', 'km'], title: t('KM')}] : []), { key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_id, unit_name }) => `${adult_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_id, unit_name }) => `${child_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, // {key: 'unit', title: t('Unit'), }, @@ -145,6 +146,7 @@ const TypesPanels = (props) => { children: ( r.concat( From 07b196fc6685383112a60698889cc46e7f75c880 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 27 Jun 2024 15:03:10 +0800 Subject: [PATCH 39/46] =?UTF-8?q?test:=20=E8=B0=83=E6=95=B4antd=E9=97=B4?= =?UTF-8?q?=E8=B7=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/App.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/App.jsx b/src/views/App.jsx index 3541d9b..b4fb2e2 100644 --- a/src/views/App.jsx +++ b/src/views/App.jsx @@ -90,6 +90,8 @@ function App() { theme={{ token: { colorPrimary: '#00b96b', + // "sizeStep": 3, + // "sizeUnit": 3, }, algorithm: theme.defaultAlgorithm, }}> From 64f690801ed13221c9c2cdbfbafcde2a5e72fc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Thu, 27 Jun 2024 16:12:43 +0800 Subject: [PATCH 40/46] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=202.=E5=AF=B9=E6=8E=A5=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=95=86=E7=BC=96=E8=BE=91=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/products.json | 2 +- src/views/products/Audit.jsx | 1 - src/views/products/Detail.jsx | 284 +++++++++++++++++--------------- 3 files changed, 149 insertions(+), 138 deletions(-) diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 55005ce..1d7ebff 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -43,7 +43,7 @@ "AuditDate": "审核时间", "productProject": "产品项目", - "Code": "代码", + "Code": "简码", "City": "城市", "Remarks": "备注", "tourTime": "游览时间", diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 72a5742..9646fd7 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -152,7 +152,6 @@ const TypesPanels = (props) => { setShowTypes(_show); setActiveKey(isEmpty(_show) ? [] : [_show[0].key]); - return () => {}; }, [productsTypes, agencyProducts]); diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 716ffa7..1b35f80 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef,useMemo } from 'react'; import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree } from 'antd'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -6,26 +6,63 @@ import DateComponent from '@/components/date'; import { fetchJSON } from "@/utils/request"; import { type } from 'windicss/utils'; import { searchAgencyAction, getAgencyProductsAction } from '@/stores/Products/Index'; - +import { useProductsTypes } from '@/hooks/useProductsSets'; import Extras from './Detail/Extras'; - - +import { groupBy } from '@/utils/commons'; +import { useParams } from 'react-router-dom'; +import { useHTLanguageSets } from '@/hooks/useHTLanguageSets'; +// import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; function Index() { const { t } = useTranslation(); const [form] = Form.useForm(); const [editingid, setEditingid] = useState(''); const [tags, setTags] = useState(['中文', 'English']); const [isModalVisible, setIsModalVisible] = useState(false); - const [selectedTag, setSelectedTag] = useState('中文'); + const [selectedTag, setSelectedTag] = useState(2); const [saveData, setSaveData] = useState(null); const [datePickerVisible, setDatePickerVisible] = useState(false); const [currentid, setCurrentid] = useState(null); const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); - const [formState, setFormState] = useState({}); const [selectedNodeid, setSelectedNodeid] = useState(null); const [selectedDateData, setSelectedDateData] = useState({ dateRange: null, selectedDays: [] }); - const [startValue,setStartValue] = useState(null); - const [endValue,setEndValue] = useState(null); + const [treeData1, setTreeData1] = useState([]); + const productsTypes = useProductsTypes(); + const [productsData, setProductsData] = useState(null); + const [quotation, setQuotation] = useState(null); + const [lgc_details,setLgc_details] = useState(null); + const { travel_agency_id } = useParams(); + + + const fetchData = async () => { + const a = { travel_agency_id }; + const res = await getAgencyProductsAction(a); + + const groupedProducts = groupBy(res.products, (row) => row.info.product_type_id); + + const generateTreeData = (productsTypes, productsData) => { + return productsTypes.map(type => ({ + title: type.label, + key: type.value, + selectable: false, + children: (productsData[type.value] || []).map(product => ({ + title: product.info.title, + key: `${type.value}-${product.info.id}`, + })) + })); + }; + + const treeData = generateTreeData(productsTypes, groupedProducts); + setProductsData(groupedProducts); + setTreeData1(treeData); + + console.log("setTreeData1", treeData); + }; + + + useEffect(() => { + fetchData(); + }, [productsTypes]); + const productProject = [ { code: "code", name: t('products:Code') }, @@ -35,13 +72,6 @@ function Index() { { code: "recommends_rate", name: t('products:recommendationRate') } ]; - const initialData = [ - { id: '1', adult_cost: '1000', child_cost: "800", currency: 'RMB', age_type: '每人', group_size_min: 4, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, - { id: '2', adult_cost: '1200', child_cost: "900", currency: 'RMB', age_type: '每人', group_size_min: 3, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, - { id: '3', adult_cost: '1500', child_cost: "1200", currency: 'RMB', age_type: '每人', group_size_min: 2, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, - { id: '4', adult_cost: '1100', child_cost: "700", currency: 'RMB', age_type: '每人', group_size_min: 1, group_size_max: 5, use_dates_start: '2024/06/12',use_dates_end: '2024/07/22', weekdays: '' }, - ]; - const [quotation, setQuotation] = useState(initialData); const isEditing = (record) => record.id === editingid; const edit = (record) => { @@ -87,12 +117,12 @@ function Index() { id: `${quotation.length + 1}`, value: '', currency: '', - age_type: '', + unit_name: '', weekdays: '', use_dates_start: '', use_dates_end: '', - group_size_min:'', - group_size_max:'' + group_size_min: '', + group_size_max: '' }; setQuotation([...quotation, newData]); }; @@ -105,9 +135,8 @@ function Index() { const handleDateChange = ({ dateRange, selectedDays }) => { // 计算周末 - const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday','Sunday']; + const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; let weekDayCount = selectedDays.map(day => weekdays.indexOf(day) + 1).sort().join(','); - console.log('Weekday Count:', weekDayCount); if (!weekDayCount || weekDayCount.length === 0) { weekDayCount = "全年"; } @@ -124,15 +153,10 @@ function Index() { const handleDateOk = () => { const { dateRange } = selectedDateData; const dateRangeList = dateRange.split('-'); - console.log("dateRangeList",dateRangeList); const use_dates_start = dateRangeList[0]; const use_dates_end = dateRangeList[1]; - - console.log("dateRange",dateRange) - if (currentid !== null) { const newData = [...quotation]; - console.log("newData",newData) const index = newData.findIndex((item) => currentid === item.id); if (index > -1) { newData[index].use_dates_start = use_dates_start; @@ -155,7 +179,7 @@ function Index() { ); } - if (dataIndex === 'age_type' && editing) { + if (dataIndex === 'unit_name' && editing) { inputNode = (
+ ); } @@ -211,27 +234,19 @@ function Index() { ); }; - const handleInputGroupSize = (name,id, dataIndex, value ) => { - + const handleInputGroupSize = (name, id, dataIndex, value) => { + const newData = [...quotation]; - console.log("newData",newData); - console.log("value",value); const index = newData.findIndex((item) => id === item.id); if (index > -1) { const item = newData[index]; - console.log("item",item) - newData[index] = {...item,} - if(name==='group_size_min'){ + newData[index] = { ...item, } + if (name === 'group_size_min') { newData[index] = { ...item, group_size_min: value }; - console.log("newData[index]",newData[index]) - }else{ + } else { newData[index] = { ...item, group_size_max: value }; - console.log("newData[index]",newData[index]) } - // const [groupSizeMin, groupSizeMax] = value.split('-').map(val => parseInt(val.trim())); - setQuotation(newData); - console.log("setQuotation",newData) } } @@ -240,7 +255,7 @@ function Index() { { title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '10%', editable: true }, { title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '10%', editable: true }, { title: t('products:currency'), dataIndex: 'currency', width: '10%', editable: true }, - { title: t('products:Types'), dataIndex: 'age_type', width: '10%', editable: true }, + { title: t('products:Types'), dataIndex: 'unit_name', width: '10%', editable: true }, { title: t('products:number'), dataIndex: 'group_size', @@ -248,15 +263,16 @@ function Index() { editable: true, render: (_, record) => `${record.group_size_min}-${record.group_size_max}` }, - - { title: t('products:validityPeriod'), - dataIndex: 'validityPeriod', - width: '20%', + + { + title: t('products:validityPeriod'), + dataIndex: 'validityPeriod', + width: '20%', editable: true, render: (_, record) => `${record.use_dates_start}-${record.use_dates_end}` }, - { title: t('products:weekend'), dataIndex: 'weekdays', width: '10%' }, + { title: t('products:Weekdays'), dataIndex: 'weekdays', width: '10%' }, { title: t('products:operation'), @@ -271,14 +287,14 @@ function Index() { ) : ( edit(record)} style={{ marginRight: 8 }}>{t('products:edit')} - handleDelete(record.id)}> + handleDelete(record.id)}> {t('products:delete')} ); }, }, - + ]; @@ -301,6 +317,10 @@ function Index() { const handleTagClick = (tag) => { setSelectedTag(tag); + console.log("handleTagClick",tag) + // form.setFieldsValue({ + + // }) }; @@ -337,54 +357,68 @@ function Index() { //树组件方法 - const handleNodeSelect = async (_, { node }) => { - + const handleNodeSelect = (_, { node }) => { + // 如果点击的是同一个节点,不做任何操作 - if (selectedNodeid === node.id) return; - - // 保存当前表单数据 - if (selectedNodeid) { - await handleSaveForm(); - } - - // 更新选中的节点 - setSelectedNodeid(node.id); - - // 加载新节点的表单数据 - if (formState[node.id]) { - form.setFieldsValue(formState[node.id]); - setQuotation(formState[node.id].quotation || []); - setLanguageStatus(formState[node.id].lgc_details || languageStatus); - } else { - form.resetFields(); - setQuotation(initialData); - setLanguageStatus([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); - } - }; + if (selectedNodeid === node.key) return; + + const fatherKey = node.key.split('-')[0]; + let initialQuotationData = null; + let infoData = null; + let lgcDetailsData = null; + productsData[fatherKey].forEach(element => { + if (element.info.title === node.title) { + initialQuotationData = element.quotation; + infoData = element.info; + lgcDetailsData = element.lgc_details; + return true; + } + }); - const handleSaveForm = async () => { - try { - const values = await form.validateFields(); - const newFormState = { - ...formState, - [selectedNodeid]: { - ...values, - quotation, - lgc_details: languageStatus, + console.log("info",infoData) + + // 累积 lgc_details 数据 + let newLgcDetails = {}; + lgcDetailsData.forEach(element => { + newLgcDetails[element.lgc] = element; + }); + + // 一次性更新 lgc_details + setLgc_details(newLgcDetails); + + setQuotation(initialQuotationData); + + // 使用 setTimeout 确保 lgc_details 已经更新 + // setTimeout(() => { + form.setFieldsValue({ + info: { + title: infoData.title, + code: infoData.code, + product_type_name: infoData.product_type_name, + city_name: infoData.city_name, + remarks: infoData.remarks, + open_weekdays: infoData.open_weekdays, + recommends_rate: infoData.recommends_rate, + unit_name: infoData.unit_name }, - }; - setFormState(newFormState); - } catch (error) { - console.error('Validation failed:', error); - } + lgc_details: { + title: newLgcDetails[selectedTag]?.title || '', + descriptions: newLgcDetails[selectedTag]?.descriptions || '' + } + }); + // }, 0); }; + + + + const onSave = (values) => { const tempData = values; tempData['quotation'] = quotation; tempData['extras'] = bindingData; - tempData['lgc_details'] = languageStatus; + // tempData['lgc_details'] = languageStatus; setSaveData(tempData); console.log("保存的数据", tempData) }; @@ -419,7 +453,7 @@ function Index() { }) => { let inputNode = inputType === 'number' ? : ; - if (dataIndex === 'age_type' && editing) { + if (dataIndex === 'unit_name' && editing) { inputNode = ( handleChange('title', e.target.value)} + // value={findLanguageDetails(selectedTag).title} + // onChange={(e) => handleChange('title', e.target.value)} /> - + handleChange('description', e.target.value)} + // value={findLanguageDetails(selectedTag).description} + // onChange={(e) => handleChange('description', e.target.value)} /> @@ -703,8 +715,8 @@ function Index() { - From 80c1b986714463650f84d49b1b459efadd3d1c2b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 28 Jun 2024 09:19:46 +0800 Subject: [PATCH 41/46] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86:=20?= =?UTF-8?q?=E9=99=84=E5=8A=A0=E9=A1=B9=E7=9B=AE=20=E6=B7=BB=E5=8A=A0/?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Products/Index.js | 8 ++++---- src/views/products/Detail/Extras.jsx | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index fe2fb9f..a5c87cd 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -27,18 +27,18 @@ export const getAgencyProductsAction = async (param) => { }; /** - * todo: + * */ export const addProductExtraAction = async (body) => { - const { errcode, result } = await postJSON(`${HT_HOST}/products/extras`, body); + const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/products_extras_add`, body); return errcode === 0 ? true : false; }; /** - * todo: + * */ export const delProductExtrasAction = async (body) => { - const { errcode, result } = await postJSON(`${HT_HOST}/products/extras/del`, body); + const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/products_extras_del`, body); return errcode === 0 ? true : false; }; diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index 2ee8587..0f2d23c 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { App, Table, Button, Modal, Popconfirm } from 'antd'; import { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index'; -import { cloneDeep } from '@/utils/commons'; +import { cloneDeep, pick } from '@/utils/commons'; import SearchForm from '@/components/SearchForm'; import RequireAuth from '@/components/RequireAuth'; @@ -46,7 +46,7 @@ const NewAddonModal = ({ onPick, ...props }) => { title: t('products:price'), dataIndex: ['quotation', '0', 'adult_cost'], width: '10rem', - render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: 成人 儿童 + render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, }, { key: 'action', @@ -120,15 +120,15 @@ const Extras = ({ productId, onChange, ...props }) => { 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('Action')+t('Success')) : message.error(t('Action')+t('Failed')); + const _item = pick(item.info, ['id', 'title', 'code']); + const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [_item] }); + 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('Action')+t('Success')) : message.error(t('Action')+t('Failed')); + const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [item.info.id] }); + delSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`); await handleGetAgencyProductExtras(); }; From 90f84fa874644050eaa7aefd0a6b85188d89400a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Fri, 28 Jun 2024 11:48:43 +0800 Subject: [PATCH 42/46] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=EF=BC=8C=E5=A4=9A=E8=AF=AD=E7=A7=8D=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail.jsx | 114 +++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 44 deletions(-) diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 1b35f80..8f35b06 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -3,40 +3,51 @@ import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, Inp import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import DateComponent from '@/components/date'; -import { fetchJSON } from "@/utils/request"; -import { type } from 'windicss/utils'; import { searchAgencyAction, getAgencyProductsAction } from '@/stores/Products/Index'; import { useProductsTypes } from '@/hooks/useProductsSets'; import Extras from './Detail/Extras'; import { groupBy } from '@/utils/commons'; import { useParams } from 'react-router-dom'; import { useHTLanguageSets } from '@/hooks/useHTLanguageSets'; -// import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; +import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; function Index() { const { t } = useTranslation(); const [form] = Form.useForm(); const [editingid, setEditingid] = useState(''); - const [tags, setTags] = useState(['中文', 'English']); + const [tags, setTags] = useState([]); const [isModalVisible, setIsModalVisible] = useState(false); - const [selectedTag, setSelectedTag] = useState(2); + const [selectedTag, setSelectedTag] = useState(null); const [saveData, setSaveData] = useState(null); const [datePickerVisible, setDatePickerVisible] = useState(false); const [currentid, setCurrentid] = useState(null); - const [languageStatus, setLanguageStatus] = useState([{ "中文": { title: "", description: "" } }, { "English": { title: "", description: "" } }]); + const [languageStatus, setLanguageStatus] = useState(null); const [selectedNodeid, setSelectedNodeid] = useState(null); + const [remainderLanguage, setRemainderLanguage] = useState([]) const [selectedDateData, setSelectedDateData] = useState({ dateRange: null, selectedDays: [] }); const [treeData1, setTreeData1] = useState([]); const productsTypes = useProductsTypes(); const [productsData, setProductsData] = useState(null); const [quotation, setQuotation] = useState(null); const [lgc_details,setLgc_details] = useState(null); + const [languageLabel,setLanguageLabel] = useState(null); const { travel_agency_id } = useParams(); + const {language} = useDefaultLgc(); + const HTLanguageSets = useHTLanguageSets(); + useEffect(() => { + setLanguageStatus(language); + const matchedLanguage = HTLanguageSets.find(HTLanguage => HTLanguage.key === language.toString()); + const languageLabel = matchedLanguage.label + // setTags([languageLabel]) + setLanguageLabel(languageLabel) + setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString())) + }, []); + + const fetchData = async () => { const a = { travel_agency_id }; const res = await getAgencyProductsAction(a); - const groupedProducts = groupBy(res.products, (row) => row.info.product_type_id); const generateTreeData = (productsTypes, productsData) => { @@ -54,8 +65,6 @@ function Index() { const treeData = generateTreeData(productsTypes, groupedProducts); setProductsData(groupedProducts); setTreeData1(treeData); - - console.log("setTreeData1", treeData); }; @@ -317,48 +326,68 @@ function Index() { const handleTagClick = (tag) => { setSelectedTag(tag); - console.log("handleTagClick",tag) - // form.setFieldsValue({ - - // }) + const matchedLanguage = HTLanguageSets.find(language => language.label === tag); + const key = matchedLanguage ? matchedLanguage.key : null; + form.setFieldsValue({ + lgc_details: { + title: lgc_details[key] ? lgc_details[key].title : '', + descriptions: lgc_details[key] ? lgc_details[key].descriptions : '' + } + }); + setLanguageStatus(key) }; const showModal = () => setIsModalVisible(true); - const handleOk = () => setIsModalVisible(false); + const handleOk = () => { + + setTags([...tags,selectedTag]) + + console.log("handleOkvalue") + setIsModalVisible(false); + } const handleCancel = () => setIsModalVisible(false); const handleTagChange = (value) => { - if (!tags.includes(value)) { - setTags([...tags, value]); - setLanguageStatus([...languageStatus, { [value]: { title: "", description: "" } }]); - } + console.log("handleTagChange",value) setSelectedTag(value); - setIsModalVisible(false); + console.log("setSelectedTag",selectedTag) + // setLanguageStatus() + + + // if (!tags.includes(value)) { + // setTags([...tags, value]); + // setLanguageStatus([...languageStatus, { [value]: { title: "", descriptions: "" } }]); + // } + // setSelectedTag(value); + // setIsModalVisible(false); }; const handleChange = (field, value) => { - const updatedLanguageStatus = languageStatus.map(lang => { - if (lang[selectedTag]) { - return { ...lang, [selectedTag]: { ...lang[selectedTag], [field]: value } }; - } - return lang; - }); - setLanguageStatus(updatedLanguageStatus); + console.log("languageStatus",languageStatus) + console.log("...lgc_details[languageStatus]",{...lgc_details[languageStatus]}) + // 更新整个 lgc_details 对象 + const updatedLgcDetails = { + ...lgc_details, + [languageStatus]:{...lgc_details[languageStatus],[field]: value,lgc:languageStatus} + }; + setLgc_details(updatedLgcDetails) + console.log("AAAAAAAAAAAAAA", lgc_details); + }; const findLanguageDetails = (tag) => { const lang = languageStatus.find(lang => lang[tag]); - return lang ? lang[tag] : { title: "", description: "" }; + return lang ? lang[tag] : { title: "", descriptions: "" }; }; //树组件方法 const handleNodeSelect = (_, { node }) => { - + setTags([languageLabel]) // 如果点击的是同一个节点,不做任何操作 if (selectedNodeid === node.key) return; @@ -374,8 +403,6 @@ function Index() { return true; } }); - - console.log("info",infoData) // 累积 lgc_details 数据 let newLgcDetails = {}; @@ -388,8 +415,8 @@ function Index() { setQuotation(initialQuotationData); + console.log("descriptions",lgc_details) // 使用 setTimeout 确保 lgc_details 已经更新 - // setTimeout(() => { form.setFieldsValue({ info: { title: infoData.title, @@ -402,11 +429,10 @@ function Index() { unit_name: infoData.unit_name }, lgc_details: { - title: newLgcDetails[selectedTag]?.title || '', - descriptions: newLgcDetails[selectedTag]?.descriptions || '' + title: newLgcDetails[language]?.title || '', + descriptions: newLgcDetails[language]?.descriptions || '' } }); - // }, 0); }; @@ -649,28 +675,28 @@ function Index() { placeholder="选择语言" optionFilterProp="children" onChange={handleTagChange} - filterOption={(input, option) => - option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 - } > - Français - Español - Deutsch + + { + remainderLanguage.map((value, label) => ( + + {value.label} + + )) + } handleChange('title', e.target.value)} + onChange={(e) => handleChange('title', e.target.value)} /> handleChange('description', e.target.value)} + onChange={(e) => handleChange('descriptions', e.target.value)} /> From 42cb1d583febd6e986c53e369c2f00fc86f29033 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 28 Jun 2024 14:15:14 +0800 Subject: [PATCH 43/46] style: products Audit --- src/assets/global.css | 3 +++ src/hooks/useProductsSets.js | 2 +- src/views/products/Audit.jsx | 14 +++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/assets/global.css b/src/assets/global.css index ca0ad13..65679f1 100644 --- a/src/assets/global.css +++ b/src/assets/global.css @@ -21,3 +21,6 @@ justify-content: center; width: 100%; } +.ant-table-wrapper.border-collapse table { + border-collapse: collapse; +} diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 015fa8a..2917489 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -51,10 +51,10 @@ export const useProductsTypes = () => { { label: t('products:type.UltraService'), 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.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'R', key: 'R' }, { label: t('products:type.Extras'), value: '8', key: '8' }, + { label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路 ]; setTypes(newData); }, [i18n.language]); diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index 803507f..825f956 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -84,8 +84,15 @@ const PriceTable = ({ productType, dataSource, refresh }) => { }); }; + const rowStyle = (r, tri) => { + const trCls = tri%2 !== 0 ? ' bg-stone-50' : ''; + const [infoI, quoteI] = r.rowSpanI; + const bigTrCls = quoteI === 0 && tri !== 0 ? 'border-collapse border-double border-0 border-t-4 border-stone-300' : ''; + return [trCls, bigTrCls].join(' '); + }; + const columns = [ - { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '' }, + { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan, }), className: 'bg-white', render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '' }, ...(productType === 'B' ? [{ key: 'km', dataIndex: ['info', 'km'], title: t('KM')}] : []), { key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_id, unit_name }) => `${adult_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_id, unit_name }) => `${child_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, @@ -122,7 +129,7 @@ const PriceTable = ({ productType, dataSource, refresh }) => { ) : null, }, ]; - return
{children} - + + handleInputGroupSize('group_size_min', record.id, 'group_size', value)} + style={{ width: '50%', marginRight: '10px' }} + /> + - + handleInputGroupSize('group_size_max', record.id, 'group_size', value)} + style={{ width: '50%', marginLeft: '10px' }} + /> + {editing ? ( @@ -146,13 +208,53 @@ function Index() { ); }; + const handleInputGroupSize = (name,id, dataIndex, value ) => { + + const newData = [...quotation]; + console.log("newData",newData); + console.log("value",value); + const index = newData.findIndex((item) => id === item.id); + if (index > -1) { + const item = newData[index]; + console.log("item",item) + newData[index] = {...item,} + if(name==='group_size_min'){ + newData[index] = { ...item, group_size_min: value }; + console.log("newData[index]",newData[index]) + }else{ + newData[index] = { ...item, group_size_max: value }; + console.log("newData[index]",newData[index]) + } + // const [groupSizeMin, groupSizeMax] = value.split('-').map(val => parseInt(val.trim())); + + setQuotation(newData); + console.log("setQuotation",newData) + } + + } + const columns = [ { title: t('products:adultPrice'), dataIndex: 'adult_cost', width: '10%', editable: true }, { title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '10%', editable: true }, { title: t('products:currency'), dataIndex: 'currency', width: '10%', editable: true }, { title: t('products:Types'), dataIndex: 'age_type', width: '10%', editable: true }, - { title: t('products:number'), dataIndex: 'group_size', width: '10%', editable: true }, - { title: t('products:validityPeriod'), dataIndex: 'validityPeriod', width: '30%', editable: true }, + { + title: t('products:number'), + dataIndex: 'group_size', + width: '20%', + editable: true, + render: (_, record) => `${record.group_size_min}-${record.group_size_max}` + }, + + { title: t('products:validityPeriod'), + dataIndex: 'validityPeriod', + width: '20%', + editable: true, + render: (_, record) => `${record.use_dates_start}-${record.use_dates_end}` + }, + + { title: t('products:weekend'), dataIndex: 'weekdays', width: '10%' }, + { title: t('products:operation'), dataIndex: 'operation', @@ -160,19 +262,20 @@ function Index() { const editable = isEditing(record); return editable ? ( - handleSave(record.key)} style={{ marginRight: 8 }}>{t('products:save')} + handleSave(record.id)} style={{ marginRight: 8 }}>{t('products:save')} {t('products:cancel')} ) : ( - edit(record)} style={{ marginRight: 8 }}>{t('products:edit')} - handleDelete(record.key)}> + edit(record)} style={{ marginRight: 8 }}>{t('products:edit')} + handleDelete(record.id)}> {t('products:delete')} ); }, }, + ]; @@ -234,21 +337,21 @@ function Index() { const handleNodeSelect = async (_, { node }) => { // 如果点击的是同一个节点,不做任何操作 - if (selectedNodeKey === node.key) return; + if (selectedNodeid === node.id) return; // 保存当前表单数据 - if (selectedNodeKey) { + if (selectedNodeid) { await handleSaveForm(); } // 更新选中的节点 - setSelectedNodeKey(node.key); + setSelectedNodeid(node.id); // 加载新节点的表单数据 - if (formState[node.key]) { - form.setFieldsValue(formState[node.key]); - setQuotation(formState[node.key].quotation || []); - setLanguageStatus(formState[node.key].lgc_details || languageStatus); + if (formState[node.id]) { + form.setFieldsValue(formState[node.id]); + setQuotation(formState[node.id].quotation || []); + setLanguageStatus(formState[node.id].lgc_details || languageStatus); } else { form.resetFields(); setQuotation(initialData); @@ -261,7 +364,7 @@ function Index() { const values = await form.validateFields(); const newFormState = { ...formState, - [selectedNodeKey]: { + [selectedNodeid]: { ...values, quotation, lgc_details: languageStatus, @@ -286,21 +389,20 @@ function Index() { //绑定产品 const initBindingData = [ - { key: '1', title: '15', value: "美元", age_type: 'aa' }, - { key: '2', title: '15', value: "美元", age_type: 'aa' }, - { key: '3', title: '15', value: "美元", age_type: 'aa' }, - { key: '4', title: '15', value: "美元", age_type: 'aa' }, + { id: '1', title: '英文导游', value: "100", age_type: '每人' }, + { id: '2', title: '中文导游', value: "200", age_type: '每团' }, + { id: '3', title: '可陪餐费', value: "400", age_type: '每人' }, ] const [bindingData, setBindingData] = useState(initBindingData); - const isEditingBinding = (record) => record.key === editingKeyBinding; - const [editingKeyBinding, setEditingKeyBinding] = useState(''); + const isEditingBinding = (record) => record.id === editingidBinding; + const [editingidBinding, setEditingidBinding] = useState(''); const editBinding = (record) => { form.setFieldsValue({ ...record }); - setEditingKeyBinding(record.key); + setEditingidBinding(record.id); }; const cancelBinding = () => { - setEditingKeyBinding(''); + setEditingidBinding(''); }; const EditableCellBinding = ({ editing, @@ -312,7 +414,16 @@ function Index() { children, ...restProps }) => { - const inputNode = inputType === 'number' ? : ; + let inputNode = inputType === 'number' ? : ; + + if (dataIndex === 'age_type' && editing) { + inputNode = ( + + ); + } return ( {editing ? ( @@ -330,9 +441,9 @@ function Index() { ); }; const bindingColums = [ - { title: t('products:Name'), dataIndex: 'title', width: '15%', editable: true }, - { title: t('products:price'), dataIndex: 'value', width: '15%', editable: true }, - { title: t('products:Types'), dataIndex: 'age_type', width: '40%', editable: true }, + { title: t('products:Name'), dataIndex: 'title', width: '25%', editable: true }, + { title: t('products:price'), dataIndex: 'value', width: '25%', editable: true }, + { title: t('products:Types'), dataIndex: 'age_type', width: '25%', editable: true }, { title: t('products:operation'), dataIndex: 'operation', @@ -340,13 +451,13 @@ function Index() { const editable = isEditingBinding(record); return editable ? ( - handleSaveBinding(record.key)} style={{ marginRight: 8 }}>{t('products:save')} + handleSaveBinding(record.id)} style={{ marginRight: 8 }}>{t('products:save')} {t('products:cancel')} ) : ( - editBinding(record)} style={{ marginRight: 8 }}>{t('products:edit')} - handleDeleteBinding(record.key)}> + editBinding(record)} style={{ marginRight: 8 }}>{t('products:edit')} + handleDeleteBinding(record.id)}> {t('products:delete')} @@ -373,31 +484,31 @@ function Index() { const handleAddBinding = () => { const newData = { - key: `${bindingData.length + 1}`, + id: `${bindingData.length + 1}`, title: '', value: '', age_type: '', }; setBindingData([...bindingData, newData]); - setEditingKeyBinding(''); // 添加这一行 + setEditingidBinding(''); // 添加这一行 }; - const handleSaveBinding = async (key) => { + const handleSaveBinding = async (id) => { try { const row = await form.validateFields(); const newData = [...bindingData]; const { value, title, age_type } = row - const index = newData.findIndex((item) => key === item.key); + const index = newData.findIndex((item) => id === item.id); if (index > -1) { const item = newData[index]; newData.splice(index, 1, { ...item, value, title, age_type }); setBindingData(newData); - setEditingKeyBinding(''); + setEditingidBinding(''); } else { newData.push(row); setBindingData(newData); - setEditingKeyBinding(''); + setEditingidBinding(''); } } catch (errInfo) { console.log('Validate Failed:', errInfo); @@ -405,9 +516,9 @@ function Index() { }; - const handleDeleteBinding = (key) => { + const handleDeleteBinding = (id) => { const newData = [...bindingData]; - const index = newData.findIndex((item) => key === item.key); + const index = newData.findIndex((item) => id === item.id); newData.splice(index, 1); setBindingData(newData); }; @@ -421,18 +532,18 @@ function Index() { const treeData = [ { title: '综费', - key: 'zf', + // id: 'zf', selectable: false, - children: [{ title: '北京怡然假日', key: 'bjyrjr' }] + children: [{ title: '北京怡然假日', id: 'bjyrjr' }] }, { title: '车费', - key: 'cf', + // id: 'cf', selectable: false, children: [ - { title: '北京', key: 'bj' }, - { title: '天津', key: 'tj' }, - { title: '北京-天津', key: 'bj-tj-3-5' } + { title: '北京', id: 'bj' }, + { title: '天津', id: 'tj' }, + { title: '北京-天津', id: 'bj-tj-3-5' } ] } ] @@ -447,16 +558,23 @@ function Index() { }, [saveData]); useEffect(() => { - // const { errcode, result } = fetchJSON('http://127.0.0.1:4523/m1/2602949-1933890-default/Service_BaseInfoWeb/travel_agency_products'); - console.log("get请求") + searchAgencyAction('54,55').then((res)=>{ + console.log("res",res) + }) + const a = { + travel_agency_id:'1511' + } + getAgencyProductsAction(a).then((res1)=>{ + console.log("res1",res1) + }) }, []) useEffect(() => { - if (selectedNodeKey) { + if (selectedNodeid) { handleSaveForm(); } - }, [selectedNodeKey]); + }, [selectedNodeid]); return ( @@ -486,7 +604,7 @@ function Index() {

{t('products:productProject')}

{productProject.map((item, index) => ( -
- handleInputGroupSize('group_size_min', record.id, 'group_size', value)} - style={{ width: '50%', marginRight: '10px' }} - /> - - - handleInputGroupSize('group_size_max', record.id, 'group_size', value)} - style={{ width: '50%', marginLeft: '10px' }} - /> - + handleInputGroupSize('group_size_min', record.id, 'group_size', value)} + style={{ width: '50%', marginRight: '10px' }} + /> + - + handleInputGroupSize('group_size_max', record.id, 'group_size', value)} + style={{ width: '50%', marginLeft: '10px' }} + /> +
r.id} />; + return
r.id} />; }; /** @@ -148,7 +155,7 @@ const TypesPanels = (props) => { // loading={loading} productType={ele.value} dataSource={agencyProducts[ele.value].reduce( - (r, c) => + (r, c, ri) => r.concat( c.quotation.map((q, i) => ({ ...q, @@ -160,6 +167,7 @@ const TypesPanels = (props) => { info: c.info, lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}), rowSpan: i === 0 ? c.quotation.length : 0, + rowSpanI: [ri, i], })) ), [] From 6caa17ea4cc137cc54a762753cfb6405494bd37b Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 28 Jun 2024 17:11:57 +0800 Subject: [PATCH 44/46] =?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:=20+=E5=B0=8F=E7=BB=84,=20?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/common.json | 2 ++ public/locales/en/products.json | 6 ++++ public/locales/zh/common.json | 2 ++ public/locales/zh/products.json | 6 ++++ src/components/DeptSelector.jsx | 61 +++++++++++++++++++++++++++++++++ src/components/SearchForm.jsx | 59 +++++++++++++++++++++++++++---- src/hooks/useProductsSets.js | 10 +++--- src/stores/Products/Index.js | 3 +- src/views/products/Manage.jsx | 18 ++++++---- 9 files changed, 148 insertions(+), 19 deletions(-) create mode 100644 src/components/DeptSelector.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 091a95f..b228ae8 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -33,6 +33,8 @@ "Success": "Success", "Failed": "Failed", + "All": "All", + "Table": { "Total": "Total {{total}} items" }, diff --git a/public/locales/en/products.json b/public/locales/en/products.json index 0e90423..667c143 100644 --- a/public/locales/en/products.json +++ b/public/locales/en/products.json @@ -101,5 +101,11 @@ "sureCancel":"Sure you want to cancel?", "sureDelete":"Sure you want to delete?", + "CopyFormMsg": { + "requiredVendor": "Please pick a target vendor", + "requiredTypes": "Please select product types", + "requiredDept": "Please pick a owner department" + }, + "#": "#" } diff --git a/public/locales/zh/common.json b/public/locales/zh/common.json index cea837c..dd6e73e 100644 --- a/public/locales/zh/common.json +++ b/public/locales/zh/common.json @@ -33,6 +33,8 @@ "Success": "成功", "Failed": "失败", + "All": "所有", + "Table": { "Total": "共 {{total}} 条" }, diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 3fe9c47..239af49 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -102,5 +102,11 @@ "sureCancel": "确定取消?", "sureDelete":"确定删除?", + "CopyFormMsg": { + "requiredVendor": "请选择目标供应商", + "requiredTypes": "请选择产品类型", + "requiredDept": "请选择所属小组" + }, + "#": "#" } diff --git a/src/components/DeptSelector.jsx b/src/components/DeptSelector.jsx new file mode 100644 index 0000000..ab8911c --- /dev/null +++ b/src/components/DeptSelector.jsx @@ -0,0 +1,61 @@ +import { Component } from 'react'; +import { Select } from 'antd'; +// import { groups, leafGroup } from '../../libs/ht'; + +/** + * 小组 + */ +export const groups = [ + { value: '1,2,28,7,33', key: '1,2,28,7,33', label: 'GH事业部', code: 'GH', children: [1, 2, 28, 7, 33] }, + { value: '8,9,11,12,20,21', key: '8,9,11,12,20,21', label: '国际事业部', code: 'INT', children: [8, 9, 11, 12, 20, 21] }, + { value: '10,18,16,30', key: '10,18,16,30', label: '孵化学院', code: '', children: [10, 18, 16, 30] }, + { value: '1', key: '1', label: 'CH直销', code: '', children: [] }, + { value: '2', key: '2', label: 'CH大客户', code: '', children: [] }, + { value: '28', key: '28', label: 'AH亚洲项目组', code: 'AH', children: [] }, + { value: '33', key: '33', label: 'GH项目组', code: '', children: [] }, + { value: '7', key: '7', label: '市场推广', code: '', children: [] }, + { value: '8', key: '8', label: '德语', code: '', children: [] }, + { value: '9', key: '9', label: '日语', code: '', children: [] }, + { value: '11', key: '11', label: '法语', code: '', children: [] }, + { value: '12', key: '12', label: '西语', code: '', children: [] }, + { value: '20', key: '20', label: '俄语', code: '', children: [] }, + { value: '21', key: '21', label: '意语', code: '', children: [] }, + { value: '10', key: '10', label: '商旅', code: '', children: [] }, + { value: '18', key: '18', label: 'CT', code: 'CT', children: [] }, + { value: '16', key: '16', label: 'APP', code: 'APP', children: [] }, + { value: '30', key: '30', label: 'Trippest', code: 'TP', children: [] }, + { value: '31', key: '31', label: '花梨鹰', code: '', children: [] }, +]; +export const groupsMappedByCode = groups.reduce((a, c) => ({ ...a, [String(c.code || c.key)]: c }), {}); +export const groupsMappedByKey = groups.reduce((a, c) => ({ ...a, [String(c.key)]: c }), {}); +export const leafGroup = groups.slice(3); +export const overviewGroup = groups.slice(0, 3); // todo: 花梨鹰 APP Trippest + +export const DeptSelector = ({show_all, isLeaf,...props}) => { + const _show_all = ['tags', 'multiple'].includes(props.mode) ? false : show_all; + const options = isLeaf===true ? leafGroup : groups; + + return ( +
+ + + ); +}; + +const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, formLayout, loading, ...props }) => { const { t } = useTranslation(); const presets = useDatePresets(); const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]); @@ -59,6 +70,19 @@ const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, lo 'year': [ { key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') }, ], + 'products_types': { + key: 'products_types', + transform: (value) => { + return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : ''; + }, + }, + 'dept': { + key: 'dept', + transform: (value) => { + console.log(value); + return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : ''; + }, + }, }; let dest = {}; const { dates, ...omittedValue } = values; @@ -106,9 +130,14 @@ const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, lo setFormValuesToSub(dest); // console.log('form onValuesChange', Object.keys(changedValues), args); }; + + const onFinishFailed = ({ values, errorFields }) => { + console.log('form validate failed', '\nform values:', values, '\nerrorFields', errorFields); + }; + return ( <> -
+ {/* */} {getFields({ sort, initialValue: readValues, hides, shows, fieldProps, fieldComProps, form, presets, t })} @@ -211,7 +240,7 @@ function getFields(props) { 'username', 99, - + , fieldProps?.username?.col || 4 ), @@ -219,7 +248,7 @@ function getFields(props) { 'realname', 99, - + , fieldProps?.realname?.col || 4 ), @@ -253,10 +282,26 @@ function getFields(props) { 'audit_state', 99, - + , fieldProps?.audit_state?.col || 3 ), + item( + 'products_types', + 99, + + + , + fieldProps?.products_types?.col || 6 + ), + item( + 'dept', + 99, + + + , + fieldProps?.dept?.col || 6 + ), ]; baseChildren = baseChildren .map((x) => { diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 2917489..888ad43 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -18,7 +18,7 @@ import { PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/confi * * 酒店 A * * 超公里 B * * 餐费 C - * * 小包价 D + * * 小包价 D // 包价线路 * * 站 X * * 购物 S * * 餐 R (餐厅) @@ -41,11 +41,12 @@ import { PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/confi * * 车费 J */ -export const useProductsTypes = () => { +export const useProductsTypes = (showAll = false) => { const [types, setTypes] = useState([]); const { t, i18n } = useTranslation(); useEffect(() => { + const allItem = [{ label: t('All'), value: '', key: '' }]; const newData = [ { label: t('products:type.Experience'), value: '6', key: '6' }, { label: t('products:type.UltraService'), value: 'B', key: 'B' }, @@ -54,9 +55,10 @@ export const useProductsTypes = () => { { label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Meals'), value: 'R', key: 'R' }, { label: t('products:type.Extras'), value: '8', key: '8' }, - { label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路 + { label: t('products:type.Package'), value: 'D', key: 'D' }, ]; - setTypes(newData); + const res = showAll ? [...allItem, ...newData] : newData; + setTypes(res); }, [i18n.language]); return types; diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index a5c87cd..02989f6 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -10,8 +10,7 @@ export const searchAgencyAction = async (param) => { return errcode !== 0 ? [] : result; }; -export const copyAgencyDataAction = async (from, to) => { - const postbody = { source_agency: from, target_agency: to }; +export const copyAgencyDataAction = async (postbody) => { const formData = new FormData(); Object.keys(postbody).forEach((key) => { formData.append(key, postbody[key]); diff --git a/src/views/products/Manage.jsx b/src/views/products/Manage.jsx index 65cfc6b..524a4ea 100644 --- a/src/views/products/Manage.jsx +++ b/src/views/products/Manage.jsx @@ -26,9 +26,11 @@ function Index() { const [copyModalVisible, setCopyModalVisible] = useState(false); const [sourceAgency, setSourceAgency] = useState({}); const [copyLoading, setCopyLoading] = useState(false); - const handleCopyAgency = async (toID) => { + const handleCopyAgency = async (param) => { setCopyLoading(true); - const success = await copyAgencyDataAction(sourceAgency.travel_agency_id, toID); + const postbody = objectMapper(param, { agency: 'target_agency', }); + const toID = postbody.target_agency; + const success = await copyAgencyDataAction({...postbody, source_agency: sourceAgency.travel_agency_id}); setCopyLoading(false); success ? message.success('复制成功') : message.error('复制失败'); @@ -70,7 +72,7 @@ function Index() { { - handleCopyAgency(formVal.agency); + handleCopyAgency(formVal); }} /> From ed651ff3d4e5ec17946919beb751fb7dd7cb5b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Mon, 1 Jul 2024 15:05:14 +0800 Subject: [PATCH 45/46] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=A1=86=202.=E5=8A=A8=E6=80=81=E6=98=BE=E7=A4=BA=E4=BA=A7?= =?UTF-8?q?=E5=93=81=E9=A1=B9=E7=9B=AE=E4=BF=A1=E6=81=AF=203.=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=8F=98=E9=87=8F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/date.jsx | 4 +- src/views/products/Detail.jsx | 511 ++++++++++++++++------------------ 2 files changed, 237 insertions(+), 278 deletions(-) diff --git a/src/components/date.jsx b/src/components/date.jsx index fec0402..ecc674f 100644 --- a/src/components/date.jsx +++ b/src/components/date.jsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { DatePicker, Button } from 'antd'; -const DateComponent = ({ onDateChange }) => { +const Date = ({ onDateChange }) => { const dateFormat = 'YYYY/MM/DD'; const { RangePicker } = DatePicker; const [dateRange, setDateRange] = useState(null); @@ -48,4 +48,4 @@ const DateComponent = ({ onDateChange }) => { ); }; -export default DateComponent; +export default Date; diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 8f35b06..74bac6a 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,8 +1,8 @@ -import React, { useState, useEffect, useRef,useMemo } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree } from 'antd'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import DateComponent from '@/components/date'; +import Date from '@/components/date'; import { searchAgencyAction, getAgencyProductsAction } from '@/stores/Products/Index'; import { useProductsTypes } from '@/hooks/useProductsSets'; import Extras from './Detail/Extras'; @@ -10,7 +10,7 @@ import { groupBy } from '@/utils/commons'; import { useParams } from 'react-router-dom'; import { useHTLanguageSets } from '@/hooks/useHTLanguageSets'; import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; -function Index() { +function Detail() { const { t } = useTranslation(); const [form] = Form.useForm(); const [editingid, setEditingid] = useState(''); @@ -28,11 +28,70 @@ function Index() { const productsTypes = useProductsTypes(); const [productsData, setProductsData] = useState(null); const [quotation, setQuotation] = useState(null); - const [lgc_details,setLgc_details] = useState(null); - const [languageLabel,setLanguageLabel] = useState(null); + const [lgc_details, setLgc_details] = useState(null); + const [languageLabel, setLanguageLabel] = useState(null); const { travel_agency_id } = useParams(); - const {language} = useDefaultLgc(); + const { language } = useDefaultLgc(); const HTLanguageSets = useHTLanguageSets(); + const { Search } = Input; + + + const [expandedKeys, setExpandedKeys] = useState([]); + const [searchValue, setSearchValue] = useState(''); + const [autoExpandParent, setAutoExpandParent] = useState(true); + const [dataList, setDataList] = useState([]); + const [defaultData, setDefaultData] = useState([]); + + + const productProject = { + "6": [], + "B": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "km", name: t('products:KM') }, + { code: "remarks", name: t('products:Remarks') } + ], + "J": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "recommends_rate", name: t('products:recommendationRate') }, + { code: "duration", name: t('products:Duration') }, + { code: "dept_name", name: t('products:Dept') }, + { code: "display_to_c", name: t('products:DisplayToC') }, + { code: "remarks", name: t('products:Remarks') }, + ], + "Q": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "recommends_rate", name: t('products:recommendationRate') }, + { code: "duration", name: t('products:Duration') }, + { code: "dept_name", name: t('products:Dept') }, + { code: "display_to_c", name: t('products:DisplayToC') }, + { code: "remarks", name: t('products:Remarks') }, + ], + "D": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "recommends_rate", name: t('products:recommendationRate') }, + { code: "duration", name: t('products:Duration') }, + { code: "dept_name", name: t('products:Dept') }, + { code: "display_to_c", name: t('products:DisplayToC') }, + { code: "remarks", name: t('products:Remarks') }, + ], + "7": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + { code: "recommends_rate", name: t('products:recommendationRate') }, + { code: "duration", name: t('products:Duration') }, + { code: "open_weekdays", name: t('products:OpenWeekdays') }, + { code: "remarks", name: t('products:Remarks') }, + ], + "R": [ + { code: "code", name: t('products:Code') }, + { code: "city_name", name: t('products:City') }, + ] + } + const [selectedCategory, setSelectedCategory] = useState(productProject.B); useEffect(() => { setLanguageStatus(language); @@ -43,43 +102,116 @@ function Index() { setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString())) }, []); - - - const fetchData = async () => { - const a = { travel_agency_id }; - const res = await getAgencyProductsAction(a); - const groupedProducts = groupBy(res.products, (row) => row.info.product_type_id); - - const generateTreeData = (productsTypes, productsData) => { - return productsTypes.map(type => ({ - title: type.label, - key: type.value, - selectable: false, - children: (productsData[type.value] || []).map(product => ({ - title: product.info.title, - key: `${type.value}-${product.info.id}`, - })) - })); - }; - const treeData = generateTreeData(productsTypes, groupedProducts); - setProductsData(groupedProducts); - setTreeData1(treeData); - }; - + useEffect(() => { + const fetchData = async () => { + const a = { travel_agency_id }; + const res = await getAgencyProductsAction(a); + const groupedProducts = groupBy(res.products, (row) => row.info.product_type_id); + + const generateTreeData = (productsTypes, productsData) => { + return productsTypes.map(type => ({ + title: type.label, + key: type.value, + selectable: false, + children: (productsData[type.value] || []).map(product => ({ + title: product.info.title, + key: `${type.value}-${product.info.id}`, + })) + })); + }; + + const treeData = generateTreeData(productsTypes, groupedProducts); + console.log("treeData", treeData) + setTreeData1(treeData); + setProductsData(groupedProducts); + setDefaultData(treeData); + setDataList(flattenTreeData(treeData)); + }; + fetchData(); }, [productsTypes]); + const flattenTreeData = (tree) => { + let flatList = []; + const flatten = (nodes) => { + nodes.forEach((node) => { + flatList.push({ title: node.title, key: node.key }); + if (node.children) { + flatten(node.children); + } + }); + }; + flatten(tree); + return flatList; + }; + + const getParentKey = (key, tree) => { + let parentKey; + for (let i = 0; i < tree.length; i++) { + const node = tree[i]; + if (node.children) { + if (node.children.some((item) => item.key === key)) { + parentKey = node.key; + } else { + const pKey = getParentKey(key, node.children); + if (pKey) { + parentKey = pKey; + } + } + } + } + return parentKey; + }; + + const titleRender = (node) => { + const index = node.title.indexOf(searchValue); + const beforeStr = node.title.substr(0, index); + const afterStr = node.title.substr(index + searchValue.length); + const highlighted = ( + {searchValue} + ); + + return index > -1 ? ( + + {beforeStr} + {highlighted} + {afterStr} + + ) : ( + {node.title} + ); + }; + + const onChange = (e) => { + const { value } = e.target; + const newExpandedKeys = dataList + .filter(item => item.title.includes(value)) + .map(item => getParentKey(item.key, defaultData)) + .filter((item, i, self) => item && self.indexOf(item) === i); + console.log("newExpandedKeys", newExpandedKeys) + setExpandedKeys(newExpandedKeys); + setSearchValue(value); + setAutoExpandParent(true); + }; + + const onExpand = (keys) => { + setExpandedKeys(keys); + setAutoExpandParent(false); + }; + + // const productProject = [ + + // { code: "code", name: t('products:Code') }, + // { code: "city_name", name: t('products:City') }, + // { code: "remarks", name: t('products:Remarks') }, + // { code: "open_hours", name: t('products:tourTime') }, + // { code: "recommends_rate", name: t('products:recommendationRate') } + // ]; + - const productProject = [ - { code: "code", name: t('products:Code') }, - { code: "city_name", name: t('products:City') }, - { code: "remarks", name: t('products:Remarks') }, - { code: "open_hours", name: t('products:tourTime') }, - { code: "recommends_rate", name: t('products:recommendationRate') } - ]; const isEditing = (record) => record.id === editingid; @@ -330,8 +462,8 @@ function Index() { const key = matchedLanguage ? matchedLanguage.key : null; form.setFieldsValue({ lgc_details: { - title: lgc_details[key] ? lgc_details[key].title : '', - descriptions: lgc_details[key] ? lgc_details[key].descriptions : '' + title: lgc_details[key] ? lgc_details[key].title : '', + descriptions: lgc_details[key] ? lgc_details[key].descriptions : '' } }); setLanguageStatus(key) @@ -342,7 +474,7 @@ function Index() { const handleOk = () => { - setTags([...tags,selectedTag]) + setTags([...tags, selectedTag]) console.log("handleOkvalue") setIsModalVisible(false); @@ -352,46 +484,32 @@ function Index() { const handleTagChange = (value) => { - console.log("handleTagChange",value) + console.log("handleTagChange", value) setSelectedTag(value); - console.log("setSelectedTag",selectedTag) - // setLanguageStatus() - - - // if (!tags.includes(value)) { - // setTags([...tags, value]); - // setLanguageStatus([...languageStatus, { [value]: { title: "", descriptions: "" } }]); - // } - // setSelectedTag(value); - // setIsModalVisible(false); + console.log("setSelectedTag", selectedTag) }; const handleChange = (field, value) => { - console.log("languageStatus",languageStatus) - console.log("...lgc_details[languageStatus]",{...lgc_details[languageStatus]}) - // 更新整个 lgc_details 对象 - const updatedLgcDetails = { - ...lgc_details, - [languageStatus]:{...lgc_details[languageStatus],[field]: value,lgc:languageStatus} - }; - setLgc_details(updatedLgcDetails) - console.log("AAAAAAAAAAAAAA", lgc_details); - - }; + console.log("languageStatus", languageStatus) + console.log("...lgc_details[languageStatus]", { ...lgc_details[languageStatus] }) + // 更新整个 lgc_details 对象 + const updatedLgcDetails = { + ...lgc_details, + [languageStatus]: { ...lgc_details[languageStatus], [field]: value, lgc: languageStatus } + }; + setLgc_details(updatedLgcDetails) + console.log("AAAAAAAAAAAAAA", lgc_details); - const findLanguageDetails = (tag) => { - const lang = languageStatus.find(lang => lang[tag]); - return lang ? lang[tag] : { title: "", descriptions: "" }; }; - //树组件方法 const handleNodeSelect = (_, { node }) => { setTags([languageLabel]) // 如果点击的是同一个节点,不做任何操作 if (selectedNodeid === node.key) return; - const fatherKey = node.key.split('-')[0]; + console.log("fatherKey", fatherKey) + setSelectedCategory(productProject[fatherKey]) let initialQuotationData = null; let infoData = null; let lgcDetailsData = null; @@ -403,38 +521,43 @@ function Index() { return true; } }); - + + console.log("infoData", infoData) + // 累积 lgc_details 数据 let newLgcDetails = {}; lgcDetailsData.forEach(element => { newLgcDetails[element.lgc] = element; }); - + // 一次性更新 lgc_details setLgc_details(newLgcDetails); - + setQuotation(initialQuotationData); - - console.log("descriptions",lgc_details) + + console.log("descriptions", lgc_details) // 使用 setTimeout 确保 lgc_details 已经更新 - form.setFieldsValue({ - info: { - title: infoData.title, - code: infoData.code, - product_type_name: infoData.product_type_name, - city_name: infoData.city_name, - remarks: infoData.remarks, - open_weekdays: infoData.open_weekdays, - recommends_rate: infoData.recommends_rate, - unit_name: infoData.unit_name - }, - lgc_details: { - title: newLgcDetails[language]?.title || '', - descriptions: newLgcDetails[language]?.descriptions || '' - } - }); + form.setFieldsValue({ + info: { + title: infoData.title, + code: infoData.code, + product_type_name: infoData.product_type_name, + city_name: infoData.city_name, + remarks: infoData.remarks, + open_weekdays: infoData.open_weekdays, + recommends_rate: infoData.recommends_rate, + duration: infoData.duration, + dept: infoData.dept, + km: infoData.km, + dept_name: infoData.dept_name + }, + lgc_details: { + title: newLgcDetails[language]?.title || '', + descriptions: newLgcDetails[language]?.descriptions || '' + } + }); }; - + @@ -450,182 +573,28 @@ function Index() { }; - //绑定产品 - const initBindingData = [ - { id: '1', title: '英文导游', value: "100", age_type: '每人' }, - { id: '2', title: '中文导游', value: "200", age_type: '每团' }, - { id: '3', title: '可陪餐费', value: "400", age_type: '每人' }, - ] - const [bindingData, setBindingData] = useState(initBindingData); - const isEditingBinding = (record) => record.id === editingidBinding; - const [editingidBinding, setEditingidBinding] = useState(''); - - const editBinding = (record) => { - form.setFieldsValue({ ...record }); - setEditingidBinding(record.id); - }; - const cancelBinding = () => { - setEditingidBinding(''); - }; - const EditableCellBinding = ({ - editing, - dataIndex, - title, - inputType, - record, - index, - children, - ...restProps - }) => { - let inputNode = inputType === 'number' ? : ; - - if (dataIndex === 'unit_name' && editing) { - inputNode = ( - - ); - } - return ( -
- ); - }; - const bindingColums = [ - { title: t('products:Name'), dataIndex: 'title', width: '25%', editable: true }, - { title: t('products:price'), dataIndex: 'value', width: '25%', editable: true }, - { title: t('products:Types'), dataIndex: 'age_type', width: '25%', editable: true }, - { - title: t('products:operation'), - dataIndex: 'operation', - render: (_, record) => { - const editable = isEditingBinding(record); - return editable ? ( - - handleSaveBinding(record.id)} style={{ marginRight: 8 }}>{t('products:save')} - {t('products:cancel')} - - ) : ( - - editBinding(record)} style={{ marginRight: 8 }}>{t('products:edit')} - handleDeleteBinding(record.id)}> - {t('products:delete')} - - - ); - }, - }, - ].map(col => { - if (!col.editable) { - return col; - } - return { - ...col, - onCell: record => ({ - record, - inputType: col.dataIndex === 'value' ? 'number' : 'text', - dataIndex: col.dataIndex, - title: col.title, - editing: isEditingBinding(record), - }), - }; - }); - - - const handleAddBinding = () => { - const newData = { - id: `${bindingData.length + 1}`, - title: '', - value: '', - age_type: '', - }; - setBindingData([...bindingData, newData]); - setEditingidBinding(''); // 添加这一行 - }; - const handleSaveBinding = async (id) => { - try { - const row = await form.validateFields(); - const newData = [...bindingData]; - const { value, title, age_type } = row - const index = newData.findIndex((item) => id === item.id); - if (index > -1) { - const item = newData[index]; - newData.splice(index, 1, { ...item, value, title, age_type }); + //Effect - setBindingData(newData); - setEditingidBinding(''); - } else { - newData.push(row); - setBindingData(newData); - setEditingidBinding(''); - } - } catch (errInfo) { - console.log('Validate Failed:', errInfo); - } - }; - const handleDeleteBinding = (id) => { - const newData = [...bindingData]; - const index = newData.findIndex((item) => id === item.id); - newData.splice(index, 1); - setBindingData(newData); - }; - const componentsBinding = { - body: { - cell: EditableCellBinding, - }, - }; - const treeData = [ - { - title: '综费', - // id: 'zf', - selectable: false, - children: [{ title: '北京怡然假日', id: 'bjyrjr' }] - }, - { - title: '车费', - // id: 'cf', - selectable: false, - children: [ - { title: '北京', id: 'bj' }, - { title: '天津', id: 'tj' }, - { title: '北京-天津', id: 'bj-tj-3-5' } - ] - } - ] - - //Effect - useEffect(() => { - if (saveData) { - - } - }, [saveData]); return (
- + + @@ -644,14 +613,18 @@ function Index() { >

{t('products:productProject')}

- {productProject.map((item, index) => ( + {selectedCategory.map((item, index) => (
- + {item.code === "duration" ? ( + + ) : ( + + )} ))} - + duration @@ -676,13 +649,13 @@ function Index() { optionFilterProp="children" onChange={handleTagChange} > - + { - remainderLanguage.map((value, label) => ( - - {value.label} - - )) + remainderLanguage.map((value, label) => ( + + {value.label} + + )) } @@ -690,13 +663,13 @@ function Index() { handleChange('title', e.target.value)} + onChange={(e) => handleChange('title', e.target.value)} /> handleChange('descriptions', e.target.value)} + onChange={(e) => handleChange('descriptions', e.target.value)} /> @@ -720,20 +693,6 @@ function Index() { - {/*

{t('products:bindingProducts')}

- -
- {editing ? ( - - {inputNode} - - ) : ( - children - )} -
- - */} @@ -755,12 +714,12 @@ function Index() { onOk={handleDateOk} onCancel={() => setDatePickerVisible(false)} > - + )} ); } -export default Index; +export default Detail; From 11b5a80986aa7c551fdcff5d661aaf00d34eaaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Tue, 2 Jul 2024 14:00:56 +0800 Subject: [PATCH 46/46] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8A=A5=E4=BB=B7=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/BatchImportPrice.jsx | 243 ++++++++++++++++++++++++++++ src/views/products/Detail.jsx | 82 +++++----- 2 files changed, 288 insertions(+), 37 deletions(-) create mode 100644 src/components/BatchImportPrice.jsx diff --git a/src/components/BatchImportPrice.jsx b/src/components/BatchImportPrice.jsx new file mode 100644 index 0000000..0404beb --- /dev/null +++ b/src/components/BatchImportPrice.jsx @@ -0,0 +1,243 @@ +import React, { useState } from 'react'; +import { Table, Input, Button, DatePicker, Row, Col, Tag, Select } from 'antd'; + +const { RangePicker } = DatePicker; +const { Option } = Select; +const BatchImportPrice = () => { + const [startDate, setStartDate] = useState(null); + const [endDate, setEndDate] = useState(null); + const [startPeople, setStartPeople] = useState(1); + const [endPeople, setEndPeople] = useState(5); + const [dateRanges, setDateRanges] = useState([]); + const [peopleRanges, setPeopleRanges] = useState([]); + const [tableData, setTableData] = useState([]); + const [currency, setCurrency] = useState('RMB'); // 默认值为 RMB + const [type, setType] = useState('每人'); // 默认值为 每人 + + const handleGenerateTable = () => { + if (dateRanges.length === 0 || peopleRanges.length === 0) return; + + const newData = dateRanges.flatMap(dateRange => { + const { startDate, endDate } = dateRange; + const dates = generateDateRange(startDate, endDate); + const row = { dateRange: `${startDate.format('YYYY-MM-DD')} ~ ${endDate.format('YYYY-MM-DD')}` }; + + peopleRanges.forEach(peopleRangeString => { + const [start, end] = peopleRangeString.split('-').map(Number); + generatePeopleRange(start, end).forEach(person => { + row[`${person}_adultPrice`] = ''; + row[`${person}_childPrice`] = ''; + }); + }); + + dates.forEach(date => { + row[date] = ''; + }); + + return row; + }); + + setTableData(newData); + }; + + const generateDateRange = (start, end) => { + const dates = []; + let currentDate = start.clone(); + + while (currentDate <= end) { + dates.push(currentDate.format('YYYY-MM-DD')); + currentDate = currentDate.add(1, 'day'); + } + + return dates; + }; + + const generatePeopleRange = (start, end) => { + const range = []; + for (let i = start; i <= end; i++) { + range.push(`人等${i}`); + } + return range; + }; + + const handleCellChange = (value, dateRange, peopleRange, type) => { + const newData = [...tableData]; + const rowIndex = newData.findIndex(row => row.dateRange === dateRange); + newData[rowIndex][`${peopleRange}_${type}`] = value; + setTableData(newData); + }; + + const handleAddDateRange = () => { + if (startDate && endDate) { + const newDateRange = { startDate, endDate }; + // 检查是否已经存在相同的日期范围 + const isDateRangeExist = dateRanges.some(range => ( + range.startDate.isSame(startDate, 'day') && range.endDate.isSame(endDate, 'day') + )); + if (!isDateRangeExist) { + setDateRanges([...dateRanges, newDateRange]); + } + } + }; + + const handleAddPeopleRange = () => { + if (startPeople <= endPeople) { + const newPeopleRange = `${startPeople}-${endPeople}`; + // 检查是否已经存在相同的人员范围 + const isPeopleRangeExist = peopleRanges.includes(newPeopleRange); + if (!isPeopleRangeExist) { + setPeopleRanges([...peopleRanges, newPeopleRange]); + } + } + }; + + const handleRemoveTag = (index, type) => { + if (type === 'date') { + setDateRanges(dateRanges.filter((_, i) => i !== index)); + } else { + const removedPeopleRange = peopleRanges[index]; + setPeopleRanges(peopleRanges.filter(range => range !== removedPeopleRange)); + } + setTableData([]); + }; + + + const [adultPrice, setAdultPrice] = useState(''); + const [childPrice, setChildPrice] = useState(''); + + const handleAdultPriceChange = (value, dateRange) => { + const newData = [...tableData]; + const rowIndex = newData.findIndex(row => row.dateRange === dateRange); + newData[rowIndex]['成人价'] = value; + setTableData(newData); + }; + + const handleChildPriceChange = (value, dateRange) => { + const newData = [...tableData]; + const rowIndex = newData.findIndex(row => row.dateRange === dateRange); + newData[rowIndex]['儿童价'] = value; + setTableData(newData); + }; + + const columns = [ + { + title: '日期\\人等', + dataIndex: 'dateRange', + key: 'dateRange', + }, + ...peopleRanges.flatMap(peopleRange => ([ + { + title: peopleRange, + dataIndex: `${peopleRange}_price`, + key: `${peopleRange}_price`, + render: (text, record) => ( +
+ handleCellChange(e.target.value, record.dateRange, peopleRange, 'adultPrice')} + placeholder="成人价" + style={{ width: '45%' }} + suffix={`${currency}/${type}`} + /> + handleCellChange(e.target.value, record.dateRange, peopleRange, 'childPrice')} + placeholder="儿童价" + style={{ width: '45%' }} + suffix={`${currency}/${type}`} + /> +
+ ), + } + ])), + ]; + + return ( + <> + + +
+ + + + + + + + + { + if (dates && dates.length === 2) { + setStartDate(dates[0]); + setEndDate(dates[1]); + } else { + setStartDate(null); + setEndDate(null); + } + }} + /> + + + + + + + setStartPeople(parseInt(e.target.value, 10))} + /> + + setEndPeople(parseInt(e.target.value, 10))} + /> + + + + + + + + + + {dateRanges.map((dateRange, index) => ( + handleRemoveTag(index, 'date')}> + {`${dateRange.startDate.format('YYYY-MM-DD')} ~ ${dateRange.endDate.format('YYYY-MM-DD')}`} + + ))} + {peopleRanges.map((peopleRange, index) => ( + handleRemoveTag(index, 'people')}> + {peopleRange} + + ))} + + +
+ + ); +}; + +export default BatchImportPrice; + + + diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 74bac6a..57cd8e7 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -10,6 +10,7 @@ import { groupBy } from '@/utils/commons'; import { useParams } from 'react-router-dom'; import { useHTLanguageSets } from '@/hooks/useHTLanguageSets'; import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; +import BatchImportPrice from '@/components/BatchImportPrice'; function Detail() { const { t } = useTranslation(); const [form] = Form.useForm(); @@ -19,12 +20,13 @@ function Detail() { const [selectedTag, setSelectedTag] = useState(null); const [saveData, setSaveData] = useState(null); const [datePickerVisible, setDatePickerVisible] = useState(false); + const [batchImportPriceVisible, setBatchImportPriceVisible] = useState(false); const [currentid, setCurrentid] = useState(null); const [languageStatus, setLanguageStatus] = useState(null); const [selectedNodeid, setSelectedNodeid] = useState(null); const [remainderLanguage, setRemainderLanguage] = useState([]) const [selectedDateData, setSelectedDateData] = useState({ dateRange: null, selectedDays: [] }); - const [treeData1, setTreeData1] = useState([]); + const [treeData, setTreeData] = useState([]); const productsTypes = useProductsTypes(); const [productsData, setProductsData] = useState(null); const [quotation, setQuotation] = useState(null); @@ -99,6 +101,7 @@ function Detail() { const languageLabel = matchedLanguage.label // setTags([languageLabel]) setLanguageLabel(languageLabel) + setSelectedTag(languageLabel) setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString())) }, []); @@ -124,8 +127,7 @@ function Detail() { }; const treeData = generateTreeData(productsTypes, groupedProducts); - console.log("treeData", treeData) - setTreeData1(treeData); + setTreeData(treeData); setProductsData(groupedProducts); setDefaultData(treeData); setDataList(flattenTreeData(treeData)); @@ -202,17 +204,6 @@ function Detail() { setAutoExpandParent(false); }; - // const productProject = [ - - // { code: "code", name: t('products:Code') }, - // { code: "city_name", name: t('products:City') }, - // { code: "remarks", name: t('products:Remarks') }, - // { code: "open_hours", name: t('products:tourTime') }, - // { code: "recommends_rate", name: t('products:recommendationRate') } - // ]; - - - const isEditing = (record) => record.id === editingid; const edit = (record) => { @@ -268,6 +259,10 @@ function Detail() { setQuotation([...quotation, newData]); }; + const handleBatchImport = () => { + setBatchImportPriceVisible(true); + } + const handleDateSelect = (id) => { setCurrentid(id); setDatePickerVisible(true); @@ -309,6 +304,11 @@ function Detail() { setDatePickerVisible(false); } +const handleBatchImportOK = () => { + setBatchImportPriceVisible(false); +} + + const EditableCell = ({ editing, dataIndex, title, inputType, record, children, handleDateSelect, ...restProps }) => { let inputNode = inputType === 'number' ? : ; if (dataIndex === 'validityPeriod' && editing) { @@ -473,10 +473,15 @@ function Detail() { const showModal = () => setIsModalVisible(true); const handleOk = () => { - + if (!selectedTag) return; + if (!remainderLanguage.some(item => item.label === selectedTag)) return; + if (remainderLanguage.includes(selectedTag)) return; + let tempRemainderLanguage = remainderLanguage.filter((item)=>{ + return item.label !== selectedTag; + }) + setRemainderLanguage(tempRemainderLanguage) setTags([...tags, selectedTag]) - - console.log("handleOkvalue") + setSelectedTag(null); setIsModalVisible(false); } @@ -508,8 +513,9 @@ function Detail() { // 如果点击的是同一个节点,不做任何操作 if (selectedNodeid === node.key) return; const fatherKey = node.key.split('-')[0]; - console.log("fatherKey", fatherKey) setSelectedCategory(productProject[fatherKey]) + + console.log("remainderLanguage",remainderLanguage) let initialQuotationData = null; let infoData = null; let lgcDetailsData = null; @@ -558,30 +564,15 @@ function Detail() { }); }; - - - - - const onSave = (values) => { const tempData = values; tempData['quotation'] = quotation; - tempData['extras'] = bindingData; + // tempData['extras'] = bindingData; // tempData['lgc_details'] = languageStatus; setSaveData(tempData); console.log("保存的数据", tempData) }; - - - - //Effect - - - - - - return (
@@ -590,7 +581,7 @@ function Detail() { ))} - duration + @@ -648,7 +639,8 @@ function Detail() { placeholder="选择语言" optionFilterProp="children" onChange={handleTagChange} - > + value={remainderLanguage.some((item) => item.label === selectedTag) ? selectedTag : undefined} + > { remainderLanguage.map((value, label) => ( @@ -689,6 +681,8 @@ function Detail() { + + @@ -717,6 +711,20 @@ function Detail() { )} + + { + batchImportPriceVisible && ( + setBatchImportPriceVisible(false)} + width="80%" + > + + + ) + }
); }