You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
GHHub/src/views/products/Detail.jsx

1345 lines
48 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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, Flex } from 'antd';
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useProductsTypes, useProductsAuditStatesMapVal, useProductsTypesMapVal, useNewProductRecord } from '@/hooks/useProductsSets';
import Extras from './Detail/Extras';
import { isEmpty } from '@/utils/commons';
import SecondHeaderWrapper from '@/components/SecondHeaderWrapper';
import Header from './Detail/Header';
import YearSelector from './Detail/YearSelector'
import { useParams } from 'react-router-dom';
import useProductsStore from '@/stores/Products/Index';
import postProductsSave from '@/stores/Products/Index';
import { useHTLanguageSets } from '@/hooks/useHTLanguageSets';
import { useDefaultLgc } from '@/i18n/LanguageSwitcher';
import BatchImportPrice from './Detail/BatchImportPrice';
import dayjs from 'dayjs';
import { PlusCircleFilled, FileAddOutlined, ExportOutlined } from '@ant-design/icons';
import { DeptSelector } from '@/components/DeptSelector';
import { useDatePresets } from '@/hooks/useDatePresets';
import CitySelector from '@/components/CitySelector';
import { HT_HOST } from '@/config';
import { postJSON, postForm } from '@/utils/request';
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;
const [tags, setTags] = useState([]);
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectedTag, setSelectedTag] = useState(null);
const [batchImportPriceVisible, setBatchImportPriceVisible] = useState(false);
const [quotationTableVisible, setQuotationTableVisible] = useState(false)
const [languageStatus, setLanguageStatus] = useState(null);
const [selectedNodeid, setSelectedNodeid] = useState(null);
const [remainderLanguage, setRemainderLanguage] = useState([])
const [treeData, setTreeData] = 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, audit_state, use_year } = useParams();
const { language } = useDefaultLgc();
const HTLanguageSets = useHTLanguageSets();
const { Search } = Input;
const [addProductVisible, setAddProductVisible] = useState(false);
const [editingProduct, setEditingProduct] = useProductsStore((state) => [state.editingProduct, state.setEditingProduct]);
const [agencyProducts, setAgencyProducts,loading] = useProductsStore((state) => [state.agencyProducts, state.setAgencyProducts, state.loading]);
const { getAgencyProducts, activeAgency } = useProductsStore();
const [expandedKeys, setExpandedKeys] = useState([]);
const [searchValue, setSearchValue] = useState('');
const [autoExpandParent, setAutoExpandParent] = useState(true);
const [dataList, setDataList] = useState([]);
const [defaultData, setDefaultData] = useState([]);
const [batchImportData, setBatchImportData] = useState([]);
const [addProductType, setAddProductType] = useState('');
const [addproductName, setAddProductName] = useState('');
const [dataFetched, setDataFetched] = useState(false); // 添加一个标志位
const [selectedDays, setSelectedDays] = useState([]);
const [weekdays, setWeekdays] = useState([]);
const [info, setInfo] = useState();
const yearOptions = [];
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, });
}
const [param, setParam] = useState({ pick_year: baseYear, pick_agency: travel_agency_id });
const [pickYear, setPickYear] = useState(baseYear);
const handleYearChange = (value) => {
setPickYear(value);
setParam((pre) => ({ ...pre, ...{ pick_year: value } }));
};
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: state });
console.log("loading",loading);
console.log("AgencyProducts",agencyProducts);
// navigate(`/products/${agency}/${year}/${audit_state}/edit`);
};
const travel_agency_name = activeAgency.travel_agency_name;
const audit_state_id = activeAgency.audit_state_id;
let isCanEditable = !(audit_state_id === 1 || audit_state_id === 3 || audit_state_id === -1)
const [currentQuotationRecord, setCurrentQuotationRecord] = useState({
use_dates_start: null,
use_dates_end: null
});
const formatDate = (date) => (date ? dayjs(date) : null);
const stateMapVal = useProductsAuditStatesMapVal();
const startDate = currentQuotationRecord.use_dates_start && dayjs(currentQuotationRecord.use_dates_start).isValid()
? formatDate(currentQuotationRecord.use_dates_start)
: null;
const endDate = currentQuotationRecord.use_dates_end && dayjs(currentQuotationRecord.use_dates_end).isValid()
? formatDate(currentQuotationRecord.use_dates_end)
: null;
const [editIndex, setEditIndex] = useState(null);
const presets = useDatePresets();
const handleBatchImportData = (data) => {
setBatchImportData(data);
};
const days = [
'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'
];
const productProject = {
"6": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
],
"B": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
{ code: "km", name: t('products:KM'), nameKey: 'products:KM' },
{
code: "remarks",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Remarks')}
</RequireAuth>
),
nameKey: 'products:Remarks',
},
],
"J": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
{
code: "recommends_rate",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:recommendationRate')}
</RequireAuth>
),
nameKey: 'products:recommendationRate',
},
{ code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' },
{
code: "dept_name",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Dept')}
</RequireAuth>
),
nameKey: 'products:Dept',
},
{
code: "display_to_c",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:DisplayToC')}
</RequireAuth>
),
nameKey: 'products:DisplayToC',
},
{
code: "remarks",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Remarks')}
</RequireAuth>
),
nameKey: 'products:Remarks',
},
],
"Q": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
{
code: "recommends_rate",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:recommendationRate')}
</RequireAuth>
),
nameKey: 'products:recommendationRate',
},
{ code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' },
{
code: "dept_name",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Dept')}
</RequireAuth>
),
nameKey: 'products:Dept',
},
{
code: "display_to_c",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:DisplayToC')}
</RequireAuth>
),
nameKey: 'products:DisplayToC',
},
{
code: "remarks",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Remarks')}
</RequireAuth>
),
nameKey: 'products:Remarks',
},
],
"D": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
{
code: "recommends_rate",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:recommendationRate')}
</RequireAuth>
),
nameKey: 'products:recommendationRate',
},
{ code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' },
{
code: "dept_name",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Dept')}
</RequireAuth>
),
nameKey: 'products:Dept',
},
{
code: "display_to_c",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:DisplayToC')}
</RequireAuth>
),
nameKey: 'products:DisplayToC',
},
{
code: "remarks",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Remarks')}
</RequireAuth>
),
nameKey: 'products:Remarks',
},
],
"7": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
{
code: "recommends_rate",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:recommendationRate')}
</RequireAuth>
),
nameKey: 'products:recommendationRate',
},
{ code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' },
{ code: "open_weekdays", name: t('products:OpenWeekdays'), nameKey: 'products:OpenWeekdays' },
{
code: "remarks",
name: (
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
{t('products:Remarks')}
</RequireAuth>
),
nameKey: 'products:Remarks',
},
],
"8": [
{ code: "code", name: t('products:Code') },
{ code: "city_name", name: t('products:City') },
],
"R": [
{ code: "code", name: t('products:Code'), nameKey: 'products:Code' },
{ code: "city_name", name: t('products:City'), nameKey: 'products:City' },
]
}
const [selectedCategory, setSelectedCategory] = useState([]);
useEffect(() => {
setLanguageStatus(language);
const matchedLanguage = HTLanguageSets.find(HTLanguage => HTLanguage.key === language.toString());
const languageLabel = matchedLanguage.label
setLanguageLabel(languageLabel)
setSelectedTag(languageLabel)
// setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString()))
}, []);
useEffect(()=>{
handleGetAgencyProducts(param);
},[param]);
useEffect(() => {
editingProductQuotation();
}, [treeData, editingProduct])
const editingProductQuotation = () => {
console.log("editingProduct", editingProduct)
const editingProductID = editingProduct.id;
console.log("editingProductID", editingProductID)
let stopProgram = false;
for (const element of treeData) {
if (stopProgram) {
return;
}
const childList = element.children;
for (const product of childList) {
const childrenID = product.key.split('-')[1];
if (editingProductID == childrenID) {
const fatherKey = product.key.split('-')[0];
setSelectedCategory(productProject[fatherKey]);
stopProgram = true;
const tempInfo = product._raw.info;
const tempLgc_details = product._raw.lgc_details.find(record => record.lgc === 2);
console.log("tempLgc_details", tempLgc_details);
const tempQuotation = product._raw.quotation;
console.log({
info: tempInfo,
lgc_details: {
title: tempLgc_details.title,
description: tempLgc_details.descriptions
},
})
setQuotation(tempQuotation);
setTags([languageLabel]);
setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString()));
form.setFieldsValue({
info: tempInfo,
lgc_details: {
title: tempLgc_details.title,
description: tempLgc_details.descriptions
},
})
break;
}
}
}
}
// 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 = (
// <span style={{ color: 'red' }}>{searchValue}</span>
// );
// return index > -1 ? (
// <span>
// {beforeStr}
// {highlighted}
// {afterStr}
// </span>
// ) : (
// <span>{node.title}</span>
// );
// };
// 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);
setEditIndex(index);
setCurrentQuotationRecord(record);
};
const handleDelete = (index) => {
const newData = [...quotation];
newData.splice(index, 1);
const sortedData = [...newData].sort((a, b) => {
const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start));
const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start));
if (aValidPeriod !== bValidPeriod) {
return aValidPeriod - bValidPeriod;
}
const aGroupSize = a.group_size_max - a.group_size_min;
const bGroupSize = b.group_size_max - b.group_size_min;
return aGroupSize - bGroupSize;
});
setQuotation(sortedData);
};
const handleAdd = () => {
const newData = {
adult_cost: 0,
child_cost: 0,
currency: '',
group_size_min: 0,
group_size_max: 0,
id: '',
lastedit_changed: '',
use_dates_start: '',
use_dates_end: '',
weekdays: '',
tempKey: Math.random()
};
setQuotation([...quotation, newData]);
const index = [...quotation, newData].length - 1
edit(newData, index);
};
const handleBatchImport = () => {
setBatchImportPriceVisible(true);
}
const quotationTableVisibleOK = () => {
currentQuotationRecord.use_dates_start = dayjs(currentQuotationRecord.use_dates_start).format('YYYY-MM-DD')
currentQuotationRecord.use_dates_end = dayjs(currentQuotationRecord.use_dates_end).format('YYYY-MM-DD')
const tempQuotation = [...quotation];
tempQuotation[editIndex] = { ...currentQuotationRecord, weekdays: weekdays };
const sortedData = [...tempQuotation].sort((a, b) => {
const aValidPeriod = dayjs(a.use_dates_end).diff(dayjs(a.use_dates_start));
const bValidPeriod = dayjs(b.use_dates_end).diff(dayjs(b.use_dates_start));
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;
});
setQuotation(sortedData);
setQuotationTableVisible(false);
}
const quotationTableVisibleCancel = () => {
setQuotationTableVisible(false);
}
const handleBatchImportOK = () => {
const tempBatchImportData = batchImportData.map(item => {
const { tag, validPeriod, ...rest } = item;
return rest;
});
const newData = [...quotation, ...tempBatchImportData];
const sortedData = [...newData].sort((a, b) => {
if (a.group_size_min !== b.group_size_min) {
return a.group_size_min - b.group_size_min;
}
return a.group_size_max - b.group_size_max;
});
setQuotation(sortedData);
setBatchImportPriceVisible(false);
}
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: 'unit',
width: '10%',
editable: true,
render: (text) => (text === '0' ? '每人' : text === '1' ? '每团' : text),
},
{
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:Weekdays'), dataIndex: 'weekdays', width: '10%' },
];
// 根据条件判断是否要添加操作列
if (!isCanEditable) {
columns.push({
title: t('products:operation'),
dataIndex: 'operation',
render: (_, record, index) => {
const canEdit = record.audit_state_id === -1;
return (
<span>
{canEdit ? (
<a onClick={() => edit(record, index)} style={{ marginRight: 8 }}>{t('Edit')}</a>
) : (
<span style={{ color: 'gray', marginRight: 8 }}>{t('Edit')}</span>
)}
{canEdit ? (
<Popconfirm title={t('sureDelete')} onConfirm={() => handleDelete(index)}>
<a>{t('Delete')}</a>
</Popconfirm>
) : (
<span style={{ color: 'gray' }}>{t('Delete')}</span>
)}
</span>
);
}
});
}
const handleTagClick = (tag) => {
setSelectedTag(tag);
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 : '',
description: lgc_details[key] ? lgc_details[key].description : ''
}
});
setLanguageStatus(key)
};
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;
})
const matchedLanguage = HTLanguageSets.find(HTLanguage => HTLanguage.label === selectedTag);
const languageKey = parseInt(matchedLanguage.key)
if (!(languageKey in lgc_details)) {
const tempLgc_details = {
...lgc_details, [languageKey]: {
title: "",
lgc: languageKey,
description: "",
id: ""
}
}
setLgc_details(tempLgc_details)
}
setRemainderLanguage(tempRemainderLanguage)
setTags([...tags, selectedTag])
setSelectedTag(null);
setIsModalVisible(false);
}
const handleCancel = () => setIsModalVisible(false);
const handleTagChange = (value) => {
setSelectedTag(value);
};
const handleChange = (field, value) => {
// 更新整个 lgc_details 对象
const updatedLgcDetails = {
...lgc_details,
[languageStatus]: {
...lgc_details[languageStatus],
[field]: value,
lgc: languageStatus.toString(),
id: lgc_details[languageStatus]?.id || ''
}
};
setLgc_details(updatedLgcDetails)
};
const handleDayClick = (dayIndex) => {
const dayOfWeek = (dayIndex % 7) + 1;
setSelectedDays((prevSelectedDays) => {
const updatedDays = prevSelectedDays.includes(dayOfWeek)
? prevSelectedDays.filter((d) => d !== dayOfWeek)
: [...prevSelectedDays, dayOfWeek];
const weekdaysString = updatedDays.sort().join(',');
setWeekdays(weekdaysString);
return updatedDays;
});
};
//树组件方法
const handleNodeSelect = (_, { node }) => {
setSelectedNodeid(node.key);
// const fatherKey = node.key.split('-')[0];
const fatherKey = node._raw.info.product_type_id;
setSelectedCategory(productProject[fatherKey]);
setTags([languageLabel]);
// 如果点击的是同一个节点,不做任何操作
if (selectedNodeid === node.key) return;
setLanguageStatus(language);
const matchedLanguage = HTLanguageSets.find(HTLanguage => HTLanguage.key === language.toString());
const languageLabelRefresh = matchedLanguage.label;
setLanguageLabel(languageLabelRefresh);
setSelectedTag(languageLabelRefresh);
setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString()));
// setEditingProduct(node._raw);
if (!node._raw.info.id) {
let infoData = node._raw.info;
setInfo(node._raw.info);
const newLgcDetails = node._raw.lgc_details
setSelectedNodeid(node.key);
form.setFieldsValue({
info: {
id: infoData.id || "",
title: infoData.title || "",
code: infoData.code || "",
type: infoData.product_type_id || "",
audit_state: "-1",
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 || "",
},
lgc_details: {
lgc: language,
title: newLgcDetails[language]?.title || '',
description: newLgcDetails[language]?.description || ''
}
})
setLgc_details([])
setQuotation([{
adult_cost: 0,
child_cost: 0,
currency: '',
group_size_min: 0,
group_size_max: 0,
id: '',
lastedit_changed: '',
use_dates_start: '',
use_dates_end: '',
weekdays: '',
tempKey: Math.random()
}])
return
} else {
let initialQuotationData = null;
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 || ''
// }
// });
// }
}
};
//新增产品
const newProduct = useNewProductRecord();
// const { language } = useDefaultLgc();
const handelAddProduct = () => {
// 找到对应的产品类型节点
// const productTypeNode = treeData.find(item => item.key === addProductType);
const copyNewProduct = structuredClone(newProduct);
copyNewProduct.info.title = addproductName;
copyNewProduct.info.product_type_id = productsTypesMapVal[addProductType].value;
copyNewProduct.info.product_type_name = productsTypesMapVal[addProductType].label;
copyNewProduct.lgc_details[0].lgc = language;
setEditingProduct(copyNewProduct);
setAddProductVisible(false);
// todo: 树, active 类型节点
return false;
const tempAddData = {
info: {
id: "",
title: addproductName,
code: "",
type: addProductType,
create_date: "",
created_by: "",
travel_agency_id: "",
travel_agency_name: "",
lastedit_changed: "",
remarks: "",
duration: "",
duration_unit: "",
open_weekdays: "",
recommends_rate: "",
dept: "",
display_to_c: "",
km: "",
city_name: "",
city_id: "",
product_type_name: "",
dept_name: "",
},
lgc_details: [
],
quotation: []
}
const newChildren = [
...productTypeNode.children,
{
title: addproductName,
key: `${addProductType}-${Date.now()}`,
_raw: tempAddData
}
];
// // 创建新的 treeData 数组,确保 React 能够检测到更改
const newTreeData = treeData.map(item => {
if (item.key === addProductType) {
return {
...item,
children: newChildren,
};
}
return item;
});
// 更新 treeData
// setEditingProduct(null);
setTreeData(newTreeData);
let tempProductDataList = productsData[addProductType];
tempProductDataList.push(tempAddData);
const newProductsData = {
...productsData, // 假设使用了展开运算符来复制现有数组
[addProductType]: tempProductDataList
};
setProductsData(newProductsData);
setAddProductVisible(false);
}
const renderFormItem = (item) => {
switch (item.code) {
case "duration":
return <Input suffix="H" disabled={isCanEditable} />;
case "display_to_c":
return (
<Select disabled={isCanEditable}>
<Select.Option value={0}>在计划显示不在报价信显示</Select.Option>
<Select.Option value={1}>计划和报价信都要显示</Select.Option>
<Select.Option value={2}>计划和报价信都不用显示</Select.Option>
</Select>
);
case "dept_name":
return <DeptSelector disabled={isCanEditable} />;
case "city_name":
return <CitySelector disabled={isCanEditable} />;
default:
return <Input disabled={isCanEditable} />;
}
};
//保存产品
const onSave = async (values) => {
let tempInfo
if (info.id === "") {
tempInfo = {
...info,
...values.info,
city_name: values.info.city_name.label,
audit_state: "-1"
}
delete tempInfo.product_type_name;
delete tempInfo.dept_name;
let tempQuotation = quotation.map(element => {
const updateData = {
...element,
audit_state: "-1"
}
delete updateData.tempKey
return updateData
})
let tempLgc_details = [{ ...lgc_details }]
const tempData = {
travel_agency_id,
info: tempInfo,
quotation: tempQuotation,
lgc_details: Object.values(lgc_details)
};
console.log("tempData", tempData);
const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/agency_product_save`, tempData);
console.log("result", result);
if (errcode === 0) {
message.success("保存成功");
setDataFetched(false);
} else {
message.error(`保存失败: ${result}`);
}
return
}
tempInfo = {
...info,
...values.info,
audit_state: "-1"
}
console.log("tempInfo", tempInfo)
let tempQuotation = quotation.map(element => {
const updateData = {
...element,
audit_state: "-1"
}
return updateData
})
const tempData = {
travel_agency_id,
info: tempInfo,
quotation: tempQuotation,
lgc_details: Object.values(lgc_details)
};
console.log("tempData", tempData)
// const { errcode, result } = await postProductsSave(tempData);
const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/agency_product_save`, tempData);
if (errcode === 0) {
message.success("保存成功");
setDataFetched(false);
} else {
message.error(`保存失败: ${result}`);
}
return
};
//提交审核方法
const submitReview = async () => {
const formData = new FormData();
formData.append('use_year', use_year);
formData.append('travel_agency_id', travel_agency_id);
try {
const { errcode, result } = await postForm(`${HT_HOST}/Service_BaseInfoWeb/agency_submit`, formData);
console.log("errcode", errcode);
if (errcode === 0) {
message.success("提交审核成功");
navigate(`/products/${travel_agency_id}/${use_year}/${audit_state}/audit`);
} else {
message.error("提交审核失败");
}
console.log("result", result);
} catch (error) {
console.error("提交审核请求失败", error);
message.error("提交审核请求失败");
}
};
// const handleStateChange = (newState) => {
// console.log("newState",newState)
// if(newState === 'addProducts'){
// setAddProductVisible(true);
// }
// if(newState === 'submitReview'){
// submitReview();
// }
// };
return (
<SecondHeaderWrapper loading={loading} backTo={false} header={
<Header title={activeAgency.travel_agency_name} refresh={handleGetAgencyProducts} handleNewProduct={() => setAddProductVisible(true)} handleSubmitForAudit={submitReview} />
// <YearSelector title={activeAgency.travel_agency_name} refresh={handleGetAgencyProducts} onStateChange={handleStateChange}/>
}>
{isEmpty(agencyProducts) ? <Empty /> :
<>
<Flex gap={10}>
{/* onNodeSelect={handleNodeSelect} */}
<ProductsTree className='basis-64 sticky top-16 overflow-y-auto shrink-0' style={{ height: 'calc(100vh - 150px)' }} />
<Divider type={'vertical'} className='mx-1 h-auto' />
<div className=' flex-auto grow-0 min-w-[800px]'>
<ProductInfo />
{/* <Row> */}
{/* <Col span={6} className=' relative'> */}
{/* <Card className='w-[inherit] fixed overflow-y-auto max-h-[80%] max-w-[22%] overflow-x-auto'>
<Row>
<Search placeholder="Search" onChange={onChange} />
</Row>
<Tree
style={{ overflowX: 'auto' }}
onSelect={handleNodeSelect}
treeData={treeData}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onExpand={onExpand}
titleRender={titleRender}
/>
</Card> */}
{/* </Col> */}
{/* <Col span={18}> */}
<Form form={form} name="control-hooks" onFinish={onSave}>
{/* <Card
title={
<Breadcrumb items={[
{ title: productsTypesMapVal[editingProduct?.info?.product_type_id]?.label || editingProduct?.info?.product_type_name },
{ title: editingProduct?.info?.title || t('New') }
]} />
}
> */}
{/* <h2>{t('products:EditComponents.info')}</h2>
<Row gutter={16}>
{selectedCategory.map((item, index) => {
return (
<Col span={8} id={index} key={`${item.code}-${index}`}>
<Form.Item name={['info', item.code]} label={item.name}>
{renderFormItem(item)}
</Form.Item>
</Col>
);
})}
</Row> */}
{/* <div style={{ marginBottom: "1%", marginLeft: "3%" }}>
{tags.map(tag => (
<Tag
key={tag}
id={tag}
onClick={() => handleTagClick(tag)}
color={tag === selectedTag ? 'blue' : undefined}
style={{ cursor: 'pointer' }}
>
{tag}
</Tag>
))}
<Tag onClick={showModal} style={{ cursor: 'pointer' }}>+</Tag>
</div>
<Modal title="选择语言" open={isModalVisible} onOk={handleOk} onCancel={handleCancel}>
<Select
showSearch
style={{ width: "80%" }}
placeholder="选择语言"
optionFilterProp="children"
onChange={handleTagChange}
value={remainderLanguage.some((item) => item.label === selectedTag) ? selectedTag : undefined}
>
{
remainderLanguage.map((value, label) => (
<Select.Option key={value.label} value={value.label}>
{value.label}
</Select.Option>
))
}
</Select>
</Modal> */}
{/* <Form.Item label={t('products:Name')} name={['lgc_details', 'title']}>
<Input
style={{ width: "30%" }}
onChange={(e) => handleChange('title', e.target.value)}
disabled={isCanEditable}
/>
</Form.Item>
<Form.Item label={t('products:Description')} name={['lgc_details', 'description']}>
<Input.TextArea
rows={4}
onChange={(e) => handleChange('description', e.target.value)}
disabled={isCanEditable}
/>
</Form.Item> */}
{/* </Card> */}
{/* </Card> */}
{/* <Card style={{ width: "80%" }}> */}
{/* <h2>{t('products:supplierQuotation')}</h2> */}
{/* <Form.Item name="quotation">
<Table rowKey={'tempKey'}
bordered
dataSource={quotation}
columns={columns}
rowClassName="editable-row"
// pagination={{ onChange: cancel }}
/>
{
// !isCanEditable &&
<Button onClick={handleAdd} type="primary" style={{ marginTop: 16 }}>
{t('products:addQuotation')}
</Button>
}
{
<Button onClick={handleBatchImport} type="primary" style={{ marginTop: 16, marginLeft: 16 }}>批量添加</Button>
}
</Form.Item> */}
{/* <Button type="primary" htmlType="submit" style={{ marginTop: 16, float: "right", marginRight: "20%" }}>
{t('Save')}
</Button> */}
{/* </Card> */}
</Form>
{/* <Card style={{ width: "80%" }}> */}
<Extras productId={editingProduct?.info?.id} />
{/* </Card> */}
{/* <FloatButton icon={<PlusCircleFilled />} onClick={() => setAddProductVisible(true)} /> */}
{/*
<FloatButton.Group
trigger="hover"
type="primary"
style={{ right: 80 }}
icon={<PlusCircleFilled />}
>
<Tooltip title="增加产品">
<FloatButton icon={<FileAddOutlined />} onClick={() => setAddProductVisible(true)} />
</Tooltip>
<Tooltip title="提交审核">
<FloatButton icon={<ExportOutlined />} onClick={async () => {
const formData = new FormData();
formData.append('use_year', use_year);
formData.append('travel_agency_id', travel_agency_id);
const { errcode, result } = await postForm(`${HT_HOST}/Service_BaseInfoWeb/agency_submit`, formData);
console.log("errcode", errcode);
if (errcode === 0) {
message.success("提交审核成功");
navigate(`/products/${travel_agency_id}/${use_year}/${audit_state}/audit`);
}
console.log("result", result);
}} />
</Tooltip>
</FloatButton.Group> */}
{/* </Col> */}
{/* </Row> */}
</div>
</Flex>
{/* <Modal
title='批量设置价格'
open={batchImportPriceVisible}
onOk={handleBatchImportOK}
onCancel={() => setBatchImportPriceVisible(false)}
width={'90%'}
>
<BatchImportPrice onBatchImportData={handleBatchImportData} />
</Modal> */}
<Modal
title="增加新产品"
open={addProductVisible}
onOk={handelAddProduct}
onCancel={() => setAddProductVisible(false)}
>
<h4>选择产品类别</h4>
<Select style={{ width: "50%" }}
value={addProductType}
onChange={(value) => setAddProductType(value)}
>
{productsTypes.map((item) => (
<Select.Option key={item.key} value={item.value}>
{item.label}
</Select.Option>
))}
</Select>
<h4>新增产品名称</h4>
<Input
value={addproductName}
onChange={(e) => setAddProductName(e.target.value)}
/>
</Modal>
{/*
<Modal
title="编辑供应商报价"
visible={quotationTableVisible}
onOk={quotationTableVisibleOK}
onCancel={quotationTableVisibleCancel}
>
<h3>成人价</h3>
<InputNumber value={currentQuotationRecord.adult_cost} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, adult_cost: e })} />
<h3>儿童价</h3>
<InputNumber value={currentQuotationRecord.child_cost} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, child_cost: e })} />
<h3>币种</h3>
<Select style={{ width: "30%" }} value={currentQuotationRecord.currency} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, currency: e })}>
<Select.Option value="rmb">rmb</Select.Option>
<Select.Option value="usd">usd</Select.Option>
</Select >
<h3>类型</h3>
<Select style={{ width: "30%" }} value={currentQuotationRecord.unit} onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, unit: e })}>
<Select.Option value="0">每人</Select.Option>
<Select.Option value="1">每团</Select.Option>
</Select>
<h3>人等</h3>
<td style={{ display: 'flex', alignItems: 'center' }}>
<InputNumber
min={0}
value={currentQuotationRecord.group_size_min}
onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_min: e })}
style={{ width: '50%', marginRight: '10px' }}
/>
<span>-</span>
<InputNumber
min={0}
value={currentQuotationRecord.group_size_max}
onChange={(e) => setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_max: e })}
style={{ width: '50%', marginLeft: '10px' }}
/>
</td>
<h3>有效期</h3>
<RangePicker
allowClear={true}
inputReadOnly={true}
presets={presets}
placeholder={['From', 'Thru']}
value={startDate && endDate ? [startDate, endDate] : null}
onChange={(dates) => {
setCurrentQuotationRecord({
...currentQuotationRecord,
use_dates_start: dates[0],
use_dates_end: dates[1]
});
}}
/>
<h3>周末</h3>
{days.map((day, index) => (
<Button
key={index}
type={selectedDays.includes((index % 7) + 1) ? 'primary' : 'default'}
onClick={() => handleDayClick(index)}
style={{ margin: '5px' }}
>
{day}
</Button>
))}
</Modal> */}
</>}
</SecondHeaderWrapper>
);
}
export default Detail;