From 1dfc2d23d78fdf92cfffa3777e2c58d5d2e3232f Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 30 Jul 2024 16:01:11 +0800 Subject: [PATCH] =?UTF-8?q?todo:=20=E4=BA=A7=E5=93=81=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2:=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SecondHeaderWrapper.jsx | 2 +- src/hooks/useHTLanguageSets.js | 6 + src/hooks/useProductsSets.js | 22 +- src/utils/commons.js | 2 +- src/views/products/Detail.jsx | 492 +++++++++--------- src/views/products/Detail/Extras.jsx | 20 +- src/views/products/Detail/Header.jsx | 37 +- src/views/products/Detail/ProductInfo.jsx | 217 ++++++++ src/views/products/Detail/ProductInfoForm.jsx | 331 ++++++++++++ src/views/products/Detail/ProductInfoLgc.jsx | 163 ++++++ src/views/products/Detail/ProductsTree.jsx | 146 ++++++ 11 files changed, 1164 insertions(+), 274 deletions(-) create mode 100644 src/views/products/Detail/ProductInfo.jsx create mode 100644 src/views/products/Detail/ProductInfoForm.jsx create mode 100644 src/views/products/Detail/ProductInfoLgc.jsx create mode 100644 src/views/products/Detail/ProductsTree.jsx diff --git a/src/components/SecondHeaderWrapper.jsx b/src/components/SecondHeaderWrapper.jsx index b456bca..9a13d91 100644 --- a/src/components/SecondHeaderWrapper.jsx +++ b/src/components/SecondHeaderWrapper.jsx @@ -16,7 +16,7 @@ const HeaderWrapper = ({ children, header, loading, backTo, ...props }) => { {/* {header} */}
{header}
- + {backTo!==false && }
diff --git a/src/hooks/useHTLanguageSets.js b/src/hooks/useHTLanguageSets.js index 73b827e..cd11190 100644 --- a/src/hooks/useHTLanguageSets.js +++ b/src/hooks/useHTLanguageSets.js @@ -12,3 +12,9 @@ export const useHTLanguageSets = () => { return newData; }; + +export const useHTLanguageSetsMapVal = () => { + const stateSets = useHTLanguageSets(); + const stateMapVal = stateSets.reduce((r, c) => ({ ...r, [`${c.value}`]: c }), {}); + return stateMapVal; +}; diff --git a/src/hooks/useProductsSets.js b/src/hooks/useProductsSets.js index 8c3ea2b..710a39f 100644 --- a/src/hooks/useProductsSets.js +++ b/src/hooks/useProductsSets.js @@ -2,6 +2,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'; +import { isEmpty } from '@/utils/commons'; /** * 产品管理 相关的预设数据 @@ -100,18 +101,21 @@ export const useProductsAuditStatesMapVal = (value) => { export const useProductsTypesFieldsets = (type) => { const [isPermitted] = useAuthStore((state) => [state.isPermitted]); const infoDefault = [['code'], ['title']]; - const infoAdmin = ['remarks', 'dept', 'display_to_c']; + const infoAdmin = ['remarks', 'dept', ]; // 'display_to_c' const infoTypesMap = { - '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个图 - 'R': [['city_id',], ['description',]], - '8': [[],[]], // todo: ? + '6': [['city',],[]], + 'B': [['city', 'km'], []], + 'J': [['city', 'recommends_rate', 'duration', 'display_to_c'], ['description',]], + 'Q': [['city', 'recommends_rate', 'duration', 'display_to_c'], ['description',]], + 'D': [['city', 'recommends_rate','duration', 'display_to_c'], ['description',]], + '7': [['city', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], ['description',]], + 'R': [['city',], ['description',]], + '8': [['display_to_c'],[]], // todo: ? }; const thisTypeFieldset = (_type) => { + if (isEmpty(_type)) { + return infoDefault; + } const adminSet = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? infoAdmin : []; return [ [...infoDefault[0], ...infoTypesMap[_type][0], ...adminSet], diff --git a/src/utils/commons.js b/src/utils/commons.js index 29fd1d6..bfc5c8c 100644 --- a/src/utils/commons.js +++ b/src/utils/commons.js @@ -391,7 +391,7 @@ export function at(obj, path) { const indexes = path.split(".").map(i => i); result = [obj]; for (let i = 0; i < indexes.length; i++) { - result = [result[0][indexes[i]]]; + result = [result[0]?.[indexes[i]] || undefined]; } } return result; diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index af0ee47..2aa7a7e 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,8 +1,8 @@ import React, { useState, useEffect } from 'react'; -import { Tooltip, Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree, FloatButton, DatePicker, Spin, message, Divider,Empty } from 'antd'; +import { Tooltip, Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree, FloatButton, DatePicker, Spin, message, Divider,Empty, Flex } from 'antd'; import { Link, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; +import { useProductsTypes, useProductsAuditStatesMapVal, useProductsTypesMapVal } from '@/hooks/useProductsSets'; import Extras from './Detail/Extras'; import { isEmpty } from '@/utils/commons'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; @@ -25,8 +25,12 @@ import RequireAuth from '@/components/RequireAuth' import { PERM_ROLE_NEW } from '@/config' import { create } from 'zustand'; import { PERM_PRODUCTS_MANAGEMENT } from '@/config'; +import { usingStorage } from '@/hooks/usingStorage'; +import ProductsTree from './Detail/ProductsTree'; +import ProductInfo from './Detail/ProductInfo'; function Detail() { const { t } = useTranslation(); + const productsTypesMapVal = useProductsTypesMapVal(); const [form] = Form.useForm(); const navigate = useNavigate() const { RangePicker } = DatePicker; @@ -76,11 +80,14 @@ function Detail() { setPickYear(value); setParam((pre) => ({ ...pre, ...{ pick_year: value } })); }; - const handleGetAgencyProducts = ({pick_year, pick_agency}={}) => { - const year = pick_year || use_year; - const agency = pick_agency || travel_agency_id; + + const { travelAgencyId } = usingStorage(); + const handleGetAgencyProducts = ({pick_year, pick_agency, pick_state}={}) => { + const year = pick_year || use_year || dayjs().year(); + const agency = pick_agency || travel_agency_id || travelAgencyId; + const state = pick_state ?? audit_state; console.log("loading",loading); - getAgencyProducts({ travel_agency_id: agency, use_year: year, audit_state }); + getAgencyProducts({ travel_agency_id: agency, use_year: year, audit_state: state }); console.log("loading",loading); console.log("AgencyProducts",agencyProducts); // navigate(`/products/${agency}/${year}/${audit_state}/edit`); @@ -353,101 +360,101 @@ function Detail() { } } - useEffect(() => { - const fetchData = async () => { - if (productsTypes.length > 0 && travel_agency_id && Object.keys(agencyProducts).length > 0) { - 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}`, - _raw: product, - })), - })); - }; - const tempExpandedKeys = productsTypes.map(item => item.key); - const treeData = generateTreeData(productsTypes, agencyProducts); - setTreeData(treeData); - setExpandedKeys(tempExpandedKeys); - setProductsData(agencyProducts); - setDefaultData(treeData); - setDataList(flattenTreeData(treeData)); - setDataFetched(true); - } - }; - fetchData(); - }, [productsTypes, travel_agency_id, use_year, audit_state, agencyProducts]); - - 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); - setExpandedKeys(newExpandedKeys); - setSearchValue(value); - setAutoExpandParent(true); - }; - - const onExpand = (keys) => { - setExpandedKeys(keys); - setAutoExpandParent(false); - }; + // useEffect(() => { + // const fetchData = async () => { + // if (productsTypes.length > 0 && travel_agency_id && Object.keys(agencyProducts).length > 0) { + // 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}`, + // _raw: product, + // })), + // })); + // }; + // const tempExpandedKeys = productsTypes.map(item => item.key); + // const treeData = generateTreeData(productsTypes, agencyProducts); + // setTreeData(treeData); + // setExpandedKeys(tempExpandedKeys); + // setProductsData(agencyProducts); + // setDefaultData(treeData); + // setDataList(flattenTreeData(treeData)); + // setDataFetched(true); + // } + // }; + // fetchData(); + // }, [productsTypes, travel_agency_id, use_year, audit_state, agencyProducts]); + + // 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); + // setExpandedKeys(newExpandedKeys); + // setSearchValue(value); + // setAutoExpandParent(true); + // }; + + // const onExpand = (keys) => { + // setExpandedKeys(keys); + // setAutoExpandParent(false); + // }; const edit = (record, index) => { setQuotationTableVisible(true); @@ -684,7 +691,8 @@ function Detail() { //树组件方法 const handleNodeSelect = (_, { node }) => { setSelectedNodeid(node.key); - const fatherKey = node.key.split('-')[0]; + // const fatherKey = node.key.split('-')[0]; + const fatherKey = node._raw.info.product_type_id; setSelectedCategory(productProject[fatherKey]); setTags([languageLabel]); // 如果点击的是同一个节点,不做任何操作 @@ -753,100 +761,100 @@ function Detail() { let infoData = null; let lgcDetailsData = null; console.log("") - productsData[fatherKey].forEach(element => { - if (element.info.id === node._raw.info.id) { - initialQuotationData = element.quotation; - infoData = element.info; - lgcDetailsData = element.lgc_details.map(item => { - const newItem = { - ...item, - description: item.descriptions, - }; - delete newItem.descriptions; - return newItem; - }); - } - }); - const quotationData = initialQuotationData.map(element => { - const updatedObject = { - ...element, - unit: element.unit_id, - tempKey: Math.random() - }; - delete updatedObject.unit_name; - delete updatedObject.unit_id; - // delete updatedObject.audit_state_id; - delete updatedObject.audit_state_name; - return updatedObject; - }); - - - if (!node._raw.info.id) { - - } - // 累积 lgc_details 数据 - let newLgcDetails = {}; - if (lgcDetailsData) { - lgcDetailsData.forEach(element => { - newLgcDetails[element.lgc] = element; - }); - } - - setLgc_details(newLgcDetails); - - - let sortedData = [...quotationData].sort((a, b) => { - // 计算有效期范围大小 - const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start)); - const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start)); - - // 按照有效期范围大小升序排序 - if (aValidPeriod !== bValidPeriod) { - return aValidPeriod - bValidPeriod; - } - - // 如果有效期范围相同,则按照人数范围大小升序排序 - const aGroupSize = a.group_size_max - a.group_size_min; - const bGroupSize = b.group_size_max - b.group_size_min; - - return aGroupSize - bGroupSize; - }); - - const tempInfo = { - id: infoData.id || "", - title: infoData.title || "", - code: infoData.code || "", - type: infoData.product_type_id || "", - create_date: infoData.create_date || "", - created_by: infoData.created_by || "", - travel_agency_id: travel_agency_id || "", - travel_agency_name: activeAgency.travel_agency_name || "", - lastedit_changed: infoData.lastedit_changed || "", - remarks: infoData.remarks || "", - duration: infoData.duration || "", - duration_unit: infoData.duration_unit || "", - open_weekdays: infoData.open_weekdays || "", - recommends_rate: infoData.recommends_rate || "", - dept: infoData.dept_id || "", - display_to_c: infoData.display_to_c || "", - km: infoData.km || "", - city_name: infoData.city_name || "", - city_id: infoData.city_id || "", - product_type_name: infoData.product_type_name || "", - dept_name: infoData.dept_name || "", - }; - setInfo(tempInfo); - setQuotation(sortedData); - if (node._raw.info.id) { - form.setFieldsValue({ - info: tempInfo, - lgc_details: { - lgc: language, - title: newLgcDetails[language]?.title || '', - description: newLgcDetails[language]?.description || '' - } - }); - } + // productsData[fatherKey].forEach(element => { + // if (element.info.id === node._raw.info.id) { + // initialQuotationData = element.quotation; + // infoData = element.info; + // lgcDetailsData = element.lgc_details.map(item => { + // const newItem = { + // ...item, + // description: item.descriptions, + // }; + // delete newItem.descriptions; + // return newItem; + // }); + // } + // }); + // const quotationData = initialQuotationData.map(element => { + // const updatedObject = { + // ...element, + // unit: element.unit_id, + // tempKey: Math.random() + // }; + // delete updatedObject.unit_name; + // delete updatedObject.unit_id; + // // delete updatedObject.audit_state_id; + // delete updatedObject.audit_state_name; + // return updatedObject; + // }); + + + // if (!node._raw.info.id) { + + // } + // // 累积 lgc_details 数据 + // let newLgcDetails = {}; + // if (lgcDetailsData) { + // lgcDetailsData.forEach(element => { + // newLgcDetails[element.lgc] = element; + // }); + // } + + // setLgc_details(newLgcDetails); + + + // let sortedData = [...quotationData].sort((a, b) => { + // // 计算有效期范围大小 + // const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start)); + // const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start)); + + // // 按照有效期范围大小升序排序 + // if (aValidPeriod !== bValidPeriod) { + // return aValidPeriod - bValidPeriod; + // } + + // // 如果有效期范围相同,则按照人数范围大小升序排序 + // const aGroupSize = a.group_size_max - a.group_size_min; + // const bGroupSize = b.group_size_max - b.group_size_min; + + // return aGroupSize - bGroupSize; + // }); + + // const tempInfo = { + // id: infoData.id || "", + // title: infoData.title || "", + // code: infoData.code || "", + // type: infoData.product_type_id || "", + // create_date: infoData.create_date || "", + // created_by: infoData.created_by || "", + // travel_agency_id: travel_agency_id || "", + // travel_agency_name: activeAgency.travel_agency_name || "", + // lastedit_changed: infoData.lastedit_changed || "", + // remarks: infoData.remarks || "", + // duration: infoData.duration || "", + // duration_unit: infoData.duration_unit || "", + // open_weekdays: infoData.open_weekdays || "", + // recommends_rate: infoData.recommends_rate || "", + // dept: infoData.dept_id || "", + // display_to_c: infoData.display_to_c || "", + // km: infoData.km || "", + // city_name: infoData.city_name || "", + // city_id: infoData.city_id || "", + // product_type_name: infoData.product_type_name || "", + // dept_name: infoData.dept_name || "", + // }; + // setInfo(tempInfo); + // setQuotation(sortedData); + // if (node._raw.info.id) { + // form.setFieldsValue({ + // info: tempInfo, + // lgc_details: { + // lgc: language, + // title: newLgcDetails[language]?.title || '', + // description: newLgcDetails[language]?.description || '' + // } + // }); + // } } }; @@ -1040,24 +1048,33 @@ function Detail() { } }; - const handleStateChange = (newState) => { - console.log("newState",newState) - if(newState === 'addProducts'){ - setAddProductVisible(true); - } - if(newState === 'submitReview'){ - submitReview(); - } - }; + // const handleStateChange = (newState) => { + // console.log("newState",newState) + // if(newState === 'addProducts'){ + // setAddProductVisible(true); + // } + // if(newState === 'submitReview'){ + // submitReview(); + // } + // }; return ( - // - }> - {isEmpty(agencyProducts) ? :
- - - + setAddProductVisible(true)} handleSubmitForAudit={submitReview} /> + // + }> + {isEmpty(agencyProducts) ? : +
+ + {/* onNodeSelect={handleNodeSelect} */} + + +
+ + {/* */} + {/* */} + {/* @@ -1070,26 +1087,22 @@ function Detail() { onExpand={onExpand} titleRender={titleRender} /> - - + */} + {/* */} - + {/* */}
- 供应商 }, - { title: 综费 }, + { title: productsTypesMapVal[editingProduct?.info?.product_type_id]?.label || editingProduct?.info?.product_type_name }, { title: editingProduct?.info?.title || t('New') } ]} /> } - > -

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

+ > */} + {/*

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

{selectedCategory.map((item, index) => { - // const key = `${item.code}-${index}`; - // console.log(key); return ( @@ -1098,9 +1111,9 @@ function Detail() { ); })} - + */} -
+ {/*
{tags.map(tag => ( +
- + handleChange('title', e.target.value)} @@ -1147,7 +1160,7 @@ function Detail() { onChange={(e) => handleChange('description', e.target.value)} disabled={isCanEditable} /> - + */} {/* */} {/* */} @@ -1174,18 +1187,18 @@ function Detail() { } - - + */} + {/* */} {/* */} - + {/* */} {/* } onClick={() => setAddProductVisible(true)} /> */} - +{/* - - - + */} + {/* */} + {/* */} +
+ setBatchImportPriceVisible(false)} width="80%" @@ -1225,7 +1240,7 @@ function Detail() { setAddProductVisible(false)} > @@ -1315,7 +1330,6 @@ function Detail() {
} - // ); } export default Detail; diff --git a/src/views/products/Detail/Extras.jsx b/src/views/products/Detail/Extras.jsx index f9d82ec..318fb7d 100644 --- a/src/views/products/Detail/Extras.jsx +++ b/src/views/products/Detail/Extras.jsx @@ -9,6 +9,7 @@ import SearchForm from '@/components/SearchForm'; import RequireAuth from '@/components/RequireAuth'; import { PERM_PRODUCTS_MANAGEMENT } from '@/config'; import { useProductsTypesMapVal } from '@/hooks/useProductsSets'; +import { usingStorage } from '@/hooks/usingStorage'; const NewAddonModal = ({ onPick, ...props }) => { const { travel_agency_id, use_year } = useParams(); @@ -110,12 +111,15 @@ const Extras = ({ productId, onChange, ...props }) => { const { notification, message } = App.useApp(); const { travel_agency_id, use_year } = useParams(); + const { travelAgencyId } = usingStorage(); const [extrasData, setExtrasData] = useState([]); const handleGetAgencyProductExtras = async () => { - const data = await getAgencyProductExtrasAction({ id: productId, travel_agency_id, use_year }); - setExtrasData(data); + setExtrasData([]); + console.log('handleGetAgencyProductExtras', productId); + // const data = await getAgencyProductExtrasAction({ id: productId, travel_agency_id: travel_agency_id || travelAgencyId, use_year }); + // setExtrasData(data); }; const handleNewAddOn = async (item) => { @@ -134,21 +138,13 @@ const Extras = ({ productId, onChange, ...props }) => { }; useEffect(() => { - handleGetAgencyProductExtras(); + if (productId) handleGetAgencyProductExtras(); return () => {}; - }, []); + }, [productId]); 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].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, - // }, - // { title: t('products:Types'), dataIndex: 'age_type', width: '40%', }, { title: '', dataIndex: 'operation', diff --git a/src/views/products/Detail/Header.jsx b/src/views/products/Detail/Header.jsx index 1e30c52..0288215 100644 --- a/src/views/products/Detail/Header.jsx +++ b/src/views/products/Detail/Header.jsx @@ -3,7 +3,7 @@ import { useParams, Link, useNavigate } from 'react-router-dom'; import { App, Button, Divider, Select } from 'antd'; import { useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import { useTranslation } from 'react-i18next'; -import useProductsStore, { postProductsQuoteAuditAction, } from '@/stores/Products/Index'; +import useProductsStore, { postProductsQuoteAuditAction } from '@/stores/Products/Index'; import { isEmpty } from '@/utils/commons'; import useAuthStore from '@/stores/Auth'; import RequireAuth from '@/components/RequireAuth'; @@ -13,9 +13,9 @@ import dayjs from 'dayjs'; import VendorSelector from '@/components/VendorSelector'; import AuditStateSelector from '@/components/AuditStateSelector'; const Header = ({ refresh, ...props }) => { - const { travel_agency_id, use_year, audit_state } = useParams(); + const { travel_agency_id, use_year, audit_state } = useParams(); const { t } = useTranslation(); - const isPermitted = useAuthStore(state => state.isPermitted); + const isPermitted = useAuthStore((state) => state.isPermitted); const [activeAgency, setActiveAgency] = useProductsStore((state) => [state.activeAgency, state.setActiveAgency]); const stateMapVal = useProductsAuditStatesMapVal(); const { message, notification } = App.useApp(); @@ -25,12 +25,12 @@ const Header = ({ refresh, ...props }) => { const currentYear = dayjs().year(); const baseYear = Number(use_year === 'all' ? currentYear : use_year); for (let i = baseYear - 3; i <= baseYear + 3; i++) { - yearOptions.push({ label: i, value: i, }); + yearOptions.push({ label: i, value: i }); } - const [param, setParam] = useState({ pick_year: baseYear, pick_agency: travel_agency_id, }); + const [param, setParam] = useState({ pick_year: baseYear, pick_agency: travel_agency_id }); const [pickYear, setPickYear] = useState(baseYear); - const [pickAgency, setPickAgency] = useState({value: activeAgency.travel_agency_id, label: activeAgency.travel_agency_name }); + const [pickAgency, setPickAgency] = useState({ value: activeAgency.travel_agency_id, label: activeAgency.travel_agency_name }); const [pickAuditState, setPickAuditState] = useState(); useEffect(() => { refresh(param); @@ -45,8 +45,7 @@ const Header = ({ refresh, ...props }) => { setPickAuditState(baseState); } return () => {}; - }, [audit_state, stateMapVal]) - + }, [audit_state, stateMapVal]); const handleYearChange = (value) => { setPickYear(value); @@ -89,7 +88,7 @@ const Header = ({ refresh, ...props }) => {

{isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? ( {

{/* */} {/* */} - + {/* PERM_PRODUCTS_OFFER_PUT */} + {t('Edit')} @@ -129,8 +129,21 @@ const Header = ({ refresh, ...props }) => { {/* todo: export, 审核完成之后才能导出 */} - - {/* */} + + + {/* */} + + {/* 编辑 */} + + + + + +
); }; diff --git a/src/views/products/Detail/ProductInfo.jsx b/src/views/products/Detail/ProductInfo.jsx new file mode 100644 index 0000000..1de5dfe --- /dev/null +++ b/src/views/products/Detail/ProductInfo.jsx @@ -0,0 +1,217 @@ +import { createContext, useEffect, useState } from 'react'; +import { Breadcrumb, Form, Divider, Button, Input, Select, Row, Col } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { useProductsTypesMapVal, } from '@/hooks/useProductsSets'; +import useProductsStore from '@/stores/Products/Index'; +import RequireAuth from '@/components/RequireAuth'; +import { PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; +import DeptSelector from '@/components/DeptSelector'; +import CitySelector from '@/components/CitySelector'; +import { at, isEmpty } from '@/utils/commons'; +import ProductInfoForm from './ProductInfoForm'; + +const ProductInfo = ({ ...props }) => { + const { t } = useTranslation(); + const [form] = Form.useForm(); + const productsTypesMapVal = useProductsTypesMapVal(); + const [agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.agencyProducts, state.editingProduct, state.setEditingProduct]); + + useEffect(() => { + + return () => { + + } + }, [editingProduct]) + + + const productTypeFormItems = { + '6': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + ], + 'B': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + { key: 'km', name: t('products:KM'), nameKey: 'products:KM' }, + { + key: 'remarks', + name: {t('products:Remarks')}, + nameKey: 'products:Remarks', + }, + ], + 'J': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + { + key: 'recommends_rate', + name: {t('products:recommendationRate')}, + nameKey: 'products:recommendationRate', + }, + { key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' }, + { + key: 'dept_name', + name: {t('products:Dept')}, + nameKey: 'products:Dept', + }, + { + key: 'display_to_c', + name: {t('products:DisplayToC')}, + nameKey: 'products:DisplayToC', + }, + { + key: 'remarks', + name: {t('products:Remarks')}, + nameKey: 'products:Remarks', + }, + ], + 'Q': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + { + key: 'recommends_rate', + name: {t('products:recommendationRate')}, + nameKey: 'products:recommendationRate', + }, + { key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' }, + { + key: 'dept_name', + name: {t('products:Dept')}, + nameKey: 'products:Dept', + }, + { + key: 'display_to_c', + name: {t('products:DisplayToC')}, + nameKey: 'products:DisplayToC', + }, + { + key: 'remarks', + name: {t('products:Remarks')}, + nameKey: 'products:Remarks', + }, + ], + 'D': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + { + key: 'recommends_rate', + name: {t('products:recommendationRate')}, + nameKey: 'products:recommendationRate', + }, + { key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' }, + { + key: 'dept_name', + name: {t('products:Dept')}, + nameKey: 'products:Dept', + }, + { + key: 'display_to_c', + name: {t('products:DisplayToC')}, + nameKey: 'products:DisplayToC', + }, + { + key: 'remarks', + name: {t('products:Remarks')}, + nameKey: 'products:Remarks', + }, + ], + '7': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + { + key: 'recommends_rate', + name: {t('products:recommendationRate')}, + nameKey: 'products:recommendationRate', + }, + { key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' }, + { key: 'open_weekdays', name: t('products:OpenWeekdays'), nameKey: 'products:OpenWeekdays' }, + { + key: 'remarks', + name: {t('products:Remarks')}, + nameKey: 'products:Remarks', + }, + ], + '8': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + ], + 'R': [ + { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }, + { key: 'city_name', name: t('products:City'), nameKey: 'products:City' }, + ], + }; + const isCanEditable = false; + const renderFormItem = (item) => { + // const item = { key: 'code', name: t('products:Code'), nameKey: 'products:Code' }; + // console.log(editingProduct?.info); + if (isEmpty(editingProduct)) { + return null; + } + console.log(item.key, at(editingProduct?.info, item.key)[0]); + let InputCom = ; + switch (item.key) { + case 'duration': + InputCom = ; + break; + case 'display_to_c': + InputCom = ( + + ); + break; + case 'dept_name': + InputCom = ; + break; + case 'city_name': + InputCom = ; + break; + case 'remarks': + InputCom = ; + break; + default: + InputCom = ; + } + return ( + + + {/* {renderFormItem(item)} */} + + + + ); + }; + + const onValuesChange = (changedValues, allValues) => { + // const dest = formValuesMapper(allValues); + console.log('form onValuesChange', Object.keys(changedValues), ); + }; + const onSave = (e) => { + console.log(e); + }; + return ( + <> + + +

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

+ + {/*
+ +
+ +
+
+
*/} + + + ); +}; +export default ProductInfo; diff --git a/src/views/products/Detail/ProductInfoForm.jsx b/src/views/products/Detail/ProductInfoForm.jsx new file mode 100644 index 0000000..635e3e5 --- /dev/null +++ b/src/views/products/Detail/ProductInfoForm.jsx @@ -0,0 +1,331 @@ +import { useEffect } from 'react'; +import { Form, Input, Row, Col, Select, DatePicker, Space, Button, InputNumber, Radio, Checkbox, Divider } from 'antd'; +import { objectMapper, at } from '@/utils/commons'; +import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config'; +// import useFormStore from '@/stores/Form'; +import { useTranslation } from 'react-i18next'; +import { useWeekdays } from '@/hooks/useDatePresets'; +import DeptSelector from '@/components/DeptSelector'; +import CitySelector from '@/components/CitySelector'; + +import { fetchJSON } from '@/utils/request'; +import { HT_HOST } from '@/config'; + +// import SearchInput from './SearchInput'; +// import AuditStateSelector from './AuditStateSelector'; +import { useProductsTypesMapVal, useProductsTypesFieldsets } from '@/hooks/useProductsSets'; +import useProductsStore from '@/stores/Products/Index'; +import ProductInfoLgc from './ProductInfoLgc'; + +const { RangePicker } = DatePicker; + +const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, showSubmit, confirmText, formName, loading, ...props }) => { + const { t } = useTranslation('products'); + const [agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.agencyProducts, state.editingProduct, state.setEditingProduct]); + const weekdays = useWeekdays(); + // const [formValues, setFormValues] = useFormStore((state) => [state.formValues, state.setFormValues]); + // const [formValuesToSub, setFormValuesToSub] = useFormStore((state) => [state.formValuesToSub, state.setFormValuesToSub]); + const [form] = Form.useForm(); + const { sort, hides, fieldProps, fieldComProps } = { + sort: '', + fieldProps: '', + fieldComProps: '', + hides: [], + shows: [], + ...props.fieldsConfig, + }; + const filedsets = useProductsTypesFieldsets(editingProduct?.info?.product_type_id); + const shows = filedsets[0]; + // console.log('filedsets', filedsets, shows); + 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) : '') }, + { key: 'enddate', transform: (arrVal) => (arrVal ? arrVal[1].format(DATE_FORMAT) : '') }, + { key: 'starttime', transform: (arrVal) => (arrVal ? arrVal[0].format(DATE_FORMAT) : '') }, + { key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') }, + ], + 'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' }, + 'audit_state': { key: 'audit_state', 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 : ''; + }, + }, + 'year': [{ key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') }], + }; + let dest = {}; + const { dates, ...omittedValue } = values; + dest = { ...omittedValue, ...objectMapper(values, destinationObject) }; + for (const key in dest) { + if (Object.prototype.hasOwnProperty.call(dest, key)) { + dest[key] = typeof dest[key] === 'string' ? (dest[key] || '').trim() : dest[key]; + } + } + // omit empty + // Object.keys(dest).forEach((key) => (dest[key] == null || dest[key] === '' || dest[key].length === 0) && delete dest[key]); + return dest; + }; + + useEffect(() => { + // const dest = formValuesMapper(formValues); + // setFormValuesToSub(dest); + form.resetFields(); + form.setFieldsValue(editingProduct?.info); + form.setFieldValue('city', { value: editingProduct?.info?.city_id, label: editingProduct?.info?.city_name }); + // form.setFieldValue('open_weekdays', ['1', '2', '3', '4', '5', '6', '7']); + return () => {}; + }, [editingProduct?.info?.id]); + + const onFinish = (values) => { + console.log('Received values of form, origin form value: \n', values); + const dest = formValuesMapper(values); + console.log('form value send to onSubmit:\n', dest); + const str = new URLSearchParams(dest).toString(); + // setFormValues(values); + // setFormValuesToSub(dest); + if (typeof onSubmit === 'function') { + onSubmit(null, dest, values, str); + } + }; + + const handleReset = () => { + form.setFieldsValue({ + // 'DateType': undefined, + }); + if (typeof onReset === 'function') { + onReset(); + } + }; + const onIValuesChange = (changedValues, allValues) => { + const dest = formValuesMapper(allValues); + // setFormValues(allValues); + // setFormValuesToSub(dest); + // console.log('form onValuesChange', Object.keys(changedValues), args); + if (typeof onValuesChange === 'function') { + onValuesChange(dest); + } + }; + return ( + <> +
+ + {getFields({ sort, initialValue: editingProduct?.info, hides, shows, fieldProps, fieldComProps, form, t, dataSets: { weekdays } })} + {/* {showSubmit && ( + + + + + + )} */} + + {/* */} + + + + + {showSubmit && ( + +
+ +
+
+ )} + + + ); +}; + +function getFields(props) { + const { fieldProps, fieldComProps, form, t, dataSets } = props; + console.log('getFields', props.initialValue); + const bigCol = 4 * 2; + const midCol = 6; + const layoutProps = { + // gutter: { xs: 8, sm: 8, lg: 16 }, + lg: { span: 4 }, + md: { span: 8 }, + sm: { span: 12 }, + xs: { span: 24 }, + }; + const item = (name, sort = 0, render, col) => { + const customCol = col || 4; + const mdCol = customCol * 2; + return { + 'key': '', + sort, + name, + render, + 'hide': false, + 'col': { lg: { span: customCol }, md: { span: mdCol < 8 ? 10 : mdCol }, flex: mdCol < 8 ? '1 0' : '' }, + }; + }; + let baseChildren = []; + baseChildren = [ + item( + 'code', + 99, + + + , + fieldProps?.code?.col || 8 + ), + item( + 'city', // todo: + 99, + + + , + fieldProps?.city?.col || 8 + ), + item( + 'dept', // todo: + 99, + + + , + fieldProps?.dept?.col || 8 + ), + item( + 'duration', + 99, + + + {/* */} + , + fieldProps?.duration?.col || 8 + ), + item( + 'km', + 99, + + + , + fieldProps?.km?.col || 8 + ), + item( + 'recommends_rate', // todo: + 99, + + {/* */} + + , + fieldProps?.display_to_c?.col || 8 + ), + item( + 'remarks', + 99, + + + , + fieldProps?.remarks?.col || 24 + ), + ]; + baseChildren = baseChildren + .map((x) => { + x.hide = false; + if (props.sort === undefined) { + return x; + } + const tmpSort = props.sort; + for (const key in tmpSort) { + if (Object.prototype.hasOwnProperty.call(tmpSort, key)) { + if (x.name === key) { + x.sort = tmpSort[key]; + } + } + } + return x; + }) + .map((x) => { + if (props.hides.length === 0 && props.shows.length === 0) { + return x; + } + if (props.hides.length === 0) { + x.hide = !props.shows.includes(x.name); + } else if (props.shows.length === 0) { + x.hide = props.hides.includes(x.name); + } + return x; + }) + .filter((x) => !x.hide) + .sort((a, b) => { + return a.sort < b.sort ? -1 : 1; + }); + const children = []; + const leftStyle = {}; // { borderRight: '1px solid #dedede' }; + for (let i = 0; i < baseChildren.length; i++) { + let style = {}; // { padding: '0px 2px' }; + style = i % 2 === 0 && baseChildren[i].col === 12 ? { ...style, ...leftStyle } : style; + style = !baseChildren[i].hide ? { ...style, display: 'block' } : { ...style, display: 'none' }; + const Item = ( + + {baseChildren[i].render} + + ); + children.push(Item); + } + return children; +} + +export default InfoForm; diff --git a/src/views/products/Detail/ProductInfoLgc.jsx b/src/views/products/Detail/ProductInfoLgc.jsx new file mode 100644 index 0000000..e2af1e0 --- /dev/null +++ b/src/views/products/Detail/ProductInfoLgc.jsx @@ -0,0 +1,163 @@ +import { createContext, useEffect, useState, useRef } from 'react'; +import { Input, Tabs, Modal, Select, Form } from 'antd'; +import useProductsStore from '@/stores/Products/Index'; +import { useHTLanguageSets, useHTLanguageSetsMapVal } from '@/hooks/useHTLanguageSets'; +import RequireAuth from '@/components/RequireAuth'; +import useAuthStore from '@/stores/Auth'; +import { PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; +import { useTranslation } from 'react-i18next'; +import { useDefaultLgc } from '@/i18n/LanguageSwitcher'; +import { cloneDeep, isEmpty } from '@/utils/commons'; + +const ProductInfoLgc = ({ formInstance, ...props }) => { + const { t } = useTranslation(); + const { language: languageHT } = useDefaultLgc(); + const [isPermitted, currentUser] = useAuthStore((state) => [state.isPermitted, state.currentUser]); + const HTLanguageSetsMapVal = useHTLanguageSetsMapVal(); + const allLgcOptions = useHTLanguageSets(); + const [agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.agencyProducts, state.editingProduct, state.setEditingProduct]); + const initialItems = [ + { + label: 'Tab 1', + children: 'Content of Tab 1', + key: '1', + }, + ]; + const [activeKey, setActiveKey] = useState(); + const [items, setItems] = useState([]); + const newTabIndex = useRef(0); + useEffect(() => { + const existsLgc = (editingProduct?.lgc_details || []).map((ele) => ({ + ...ele, + label: HTLanguageSetsMapVal[ele.lgc].label, + key: `${editingProduct.info.id}-${ele.id}`, + closable: false, // isPermitted(PERM_PRODUCTS_MANAGEMENT) ? true : false, + children: ( + <> + + handleChange('title', e.target.value)} + // disabled={isCanEditable} + disabled={!isEmpty(ele.title)} + /> + + + handleChange('description', e.target.value)} + disabled={!isEmpty(ele.descriptions)} + /> + + + + + ), + })); + setItems(existsLgc); + const pageDefaultLgcI = (editingProduct?.lgc_details || []).findIndex((ele) => ele.lgc === languageHT); + setActiveKey(existsLgc?.[pageDefaultLgcI || 0]?.key); + // formInstance.validateFields(); + const filterLgcOptions = allLgcOptions.filter((ele) => !existsLgc.some((item) => `${item.lgc}` === ele.value)); + setLgcOptions(filterLgcOptions); + + return () => {}; + }, [editingProduct]); + + const onChange = (newActiveKey) => { + setActiveKey(newActiveKey); + }; + const addLgc = (lgcItem) => { + const newActiveKey = lgcItem.value; // `newTab${newTabIndex.current++}`; + const newPanes = [...items]; + newPanes.push({ + ...lgcItem, + children: ( + <> + + + + + + + + + + ), + }); + setItems(newPanes); + setActiveKey(newActiveKey); + const currentLgcOptions = cloneDeep(lgcOptions); + currentLgcOptions.splice(currentLgcOptions.findIndex(ele => ele.value === lgcItem.value), 1); + setLgcOptions(currentLgcOptions); + setSelectNewLgc(null); + }; + const remove = (targetKey) => { + let newActiveKey = activeKey; + let lastIndex = -1; + items.forEach((item, i) => { + if (item.key === targetKey) { + lastIndex = i - 1; + } + }); + const newPanes = items.filter((item) => item.key !== targetKey); + if (newPanes.length && newActiveKey === targetKey) { + if (lastIndex >= 0) { + newActiveKey = newPanes[lastIndex].key; + } else { + newActiveKey = newPanes[0].key; + } + } + setItems(newPanes); + setActiveKey(newActiveKey); + setLgcOptions([...lgcOptions, ...items.filter(item => item.key === targetKey)]); + }; + const onEdit = (targetKey, action) => { + if (action === 'add') { + setNewLgcModalVisible(true); + } else { + remove(targetKey); + } + }; + + const [newLgcModalVisible, setNewLgcModalVisible] = useState(false); + const [selectNewLgc, setSelectNewLgc] = useState(); + const [lgcOptions, setLgcOptions] = useState(allLgcOptions); + const handleOk = () => { + addLgc(selectNewLgc); + setNewLgcModalVisible(false); + }; + const handleCancel = () => { + setNewLgcModalVisible(false); + }; + const onSelectNewLgc = (lgcItem) => { + setSelectNewLgc(lgcItem); + }; + return ( + <> + + {(fields, { add, remove }) => } + + setNewLgcModalVisible(false)}> +