From fe868543ff0cea53e8ee9733ff0075ddf3c101c1 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Thu, 18 Jul 2024 14:20:16 +0800 Subject: [PATCH 01/12] =?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=E4=B8=8D=E9=99=90=E5=88=B6?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Manage.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/products/Manage.jsx b/src/views/products/Manage.jsx index 4b2f2b6..a46dab8 100644 --- a/src/views/products/Manage.jsx +++ b/src/views/products/Manage.jsx @@ -82,11 +82,12 @@ function Index() { render: (_, r) => ( {t('Edit')} - {r.audit_state_id >= 0 ? ( + {t('Audit')} + {/* {r.audit_state_id >= 0 ? ( {t('Audit')} ) : ( {t('Audit')} - )} + )} */} From f1defd23e6cfdd5cadd2906063c28107e4b1a992 Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Fri, 19 Jul 2024 09:54:00 +0800 Subject: [PATCH 02/12] =?UTF-8?q?doc:=20=E6=B3=A8=E9=87=8A=20dev=20?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E7=83=AD=E6=9B=B4=E6=96=B0=E5=87=BA=E9=94=99?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Auth.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/stores/Auth.js b/src/stores/Auth.js index d880476..83da003 100644 --- a/src/stores/Auth.js +++ b/src/stores/Auth.js @@ -64,6 +64,8 @@ const useAuthStore = create((set, get) => ({ const { startTokenInterval, loadUserPermission } = get() const { setStorage, loginToken } = usingStorage() + // Dev 模式使用 localStorage,会有 token 失效情况,需要手动删除 + // Prod 环境没有该问题 const userJson = await fetchUserDetail(loginToken) appendRequestParams('token', loginToken) @@ -142,9 +144,9 @@ const useAuthStore = create((set, get) => ({ async function checkTokenTimeout() { // TODO:Token 失效后要跳转到登录页面 const { LastReqDate } = await fetchLastRequet() - const lastReqDate = new Date(LastReqDate) + const lastReqDateTime = new Date(LastReqDate).getTime() const now = new Date() - const diffTime = now.getTime() - lastReqDate.getTime() + const diffTime = now.getTime() - lastReqDateTime const diffHours = diffTime/1000/60/60 if (diffHours > 1) { logout() From 9cb6d187c011efc518708eea2815f61fbd71884b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Fri, 19 Jul 2024 17:30:21 +0800 Subject: [PATCH 03/12] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=95=86=E5=BD=95=E5=85=A5=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E3=80=81=E4=BB=B7=E6=A0=BC=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=202.=E5=A2=9E=E5=8A=A0=E4=BE=9B=E5=BA=94=E5=95=86=E6=8F=90?= =?UTF-8?q?=E4=BA=A4=E4=BA=A7=E5=93=81=E5=AE=A1=E6=A0=B8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Products/Index.js | 5 + src/views/products/Detail.jsx | 607 +++++++++++++++++++++------------- 2 files changed, 391 insertions(+), 221 deletions(-) diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 3d727be..101a192 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -95,6 +95,11 @@ export const postProductsAuditAction = async (auditState, infoRow) => { // return errcode !== 0 ? {} : result; }; +export const postProductsSave = async (products) => { + const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/agency_product_save`, products); + return { errcode, result }; +} + const initialState = { loading: false, searchValues: {}, diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 669ce1d..fdaaf95 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,22 +1,26 @@ -import React, { useState, useEffect, useRef, useMemo } from 'react'; -import { Button, Card, Col, Row, Breadcrumb, Table, Popconfirm, Form, Input, InputNumber, Tag, Modal, Select, Tree, FloatButton, DatePicker, Spin, message } from 'antd'; +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 } from 'antd'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useProductsTypes } from '@/hooks/useProductsSets'; +import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import Extras from './Detail/Extras'; 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 } from '@ant-design/icons'; +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 } from '@/utils/request'; +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'; function Detail() { const { t } = useTranslation(); const [form] = Form.useForm(); @@ -35,8 +39,7 @@ function Detail() { const [quotation, setQuotation] = useState(null); const [lgc_details, setLgc_details] = useState(null); const [languageLabel, setLanguageLabel] = useState(null); - const [infoDataForId, setInfoDataForId] = useState(null); - const { travel_agency_id } = useParams(); + const { travel_agency_id, audit_state, use_year } = useParams(); const { language } = useDefaultLgc(); const HTLanguageSets = useHTLanguageSets(); const { Search } = Input; @@ -53,17 +56,18 @@ function Detail() { const [addProductType, setAddProductType] = useState(''); const [addproductName, setAddProductName] = useState(''); const [dataFetched, setDataFetched] = useState(false); // 添加一个标志位 - const [selectedNodeKey, setSelectedNodeKey] = useState(null); const [selectedDays, setSelectedDays] = useState([]); const [weekdays, setWeekdays] = useState([]); const [info, setInfo] = useState(); 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) @@ -80,7 +84,6 @@ function Detail() { const days = [ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]; - const productProject = { "6": [ { code: "code", name: t('products:Code'), nameKey: 'products:Code' }, @@ -90,42 +93,162 @@ function Detail() { { 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: t('products:Remarks'), nameKey: 'products:Remarks' } + { + code: "remarks", + name: ( + + {t('products:Remarks')} + + ), + 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: t('products:recommendationRate'), nameKey: 'products:recommendationRate' }, + { + code: "recommends_rate", + name: ( + + {t('products:recommendationRate')} + + ), + nameKey: 'products:recommendationRate', + }, { code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' }, - { code: "dept_name", name: t('products:Dept'), nameKey: 'products:Dept' }, - { code: "display_to_c", name: t('products:DisplayToC'), nameKey: 'products:DisplayToC' }, - { code: "remarks", name: t('products:Remarks'), nameKey: 'products:Remarks' }, + { + code: "dept_name", + name: ( + + {t('products:Dept')} + + ), + nameKey: 'products:Dept', + }, + { + code: "display_to_c", + name: ( + + {t('products:DisplayToC')} + + ), + nameKey: 'products:DisplayToC', + }, + { + code: "remarks", + name: ( + + {t('products:Remarks')} + + ), + 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: t('products:recommendationRate'), nameKey: 'products:recommendationRate' }, + { + code: "recommends_rate", + name: ( + + {t('products:recommendationRate')} + + ), + nameKey: 'products:recommendationRate', + }, { code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' }, - { code: "dept_name", name: t('products:Dept'), nameKey: 'products:Dept' }, - { code: "display_to_c", name: t('products:DisplayToC'), nameKey: 'products:DisplayToC' }, - { code: "remarks", name: t('products:Remarks'), nameKey: 'products:Remarks' }, + { + code: "dept_name", + name: ( + + {t('products:Dept')} + + ), + nameKey: 'products:Dept', + }, + { + code: "display_to_c", + name: ( + + {t('products:DisplayToC')} + + ), + nameKey: 'products:DisplayToC', + }, + { + code: "remarks", + name: ( + + {t('products:Remarks')} + + ), + 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: t('products:recommendationRate'), nameKey: 'products:recommendationRate' }, + { + code: "recommends_rate", + name: ( + + {t('products:recommendationRate')} + + ), + nameKey: 'products:recommendationRate', + }, { code: "duration", name: t('products:Duration'), nameKey: 'products:Duration' }, - { code: "dept_name", name: t('products:Dept'), nameKey: 'products:Dept' }, - { code: "display_to_c", name: t('products:DisplayToC'), nameKey: 'products:DisplayToC' }, - { code: "remarks", name: t('products:Remarks'), nameKey: 'products:Remarks' }, + { + code: "dept_name", + name: ( + + {t('products:Dept')} + + ), + nameKey: 'products:Dept', + }, + { + code: "display_to_c", + name: ( + + {t('products:DisplayToC')} + + ), + nameKey: 'products:DisplayToC', + }, + { + code: "remarks", + name: ( + + {t('products:Remarks')} + + ), + 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: t('products:recommendationRate'), nameKey: 'products:recommendationRate' }, + { + code: "recommends_rate", + name: ( + + {t('products:recommendationRate')} + + ), + 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: t('products:Remarks'), nameKey: 'products:Remarks' }, + { + code: "remarks", + name: ( + + {t('products:Remarks')} + + ), + nameKey: 'products:Remarks', + }, ], "8": [ { code: "code", name: t('products:Code') }, @@ -136,7 +259,7 @@ function Detail() { { code: "city_name", name: t('products:City'), nameKey: 'products:City' }, ] } - const [selectedCategory, setSelectedCategory] = useState(productProject.B); + const [selectedCategory, setSelectedCategory] = useState([]); useEffect(() => { setLanguageStatus(language); @@ -169,10 +292,11 @@ function Detail() { }; const tempExpandedKeys = productsTypes.map(item => item.key) const treeData = generateTreeData(productsTypes, agencyProducts); + setDataFetched(true); // 设置标志位为 true,表示数据已获取 setTreeData(treeData); setExpandedKeys(tempExpandedKeys); - console.log("agencyProducts", agencyProducts) + console.log("stateMapVal", stateMapVal); setProductsData(agencyProducts); setDefaultData(treeData); setDataList(flattenTreeData(treeData)); @@ -182,7 +306,37 @@ function Detail() { fetchData(); }, [agencyProducts, dataFetched]); - + // useEffect(()=>{ + // // if(editingProduct && treeData){ + + // editingProductQuotation(); + // // } + + // },[treeData,editingProduct]) + + // const editingProductQuotation = () => { + // console.log("editingProduct",editingProduct) + // const editingProductID = editingProduct.id; + // 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]; + // console.log("childrenID",childrenID); + // console.log("editingProductID",editingProductID); + // if (editingProductID == childrenID) { + // console.log("AAAAAAAAAA"); + // console.log("product",product); + // stopProgram = true; + // handleNodeSelect(product); + // break; + // } + // } + // } + // } const flattenTreeData = (tree) => { let flatList = []; const flatten = (nodes) => { @@ -197,6 +351,17 @@ function Detail() { return flatList; }; + // const ProductsSave = async (products) => { + // await postProductsSave(products) + // .then((json)=> { + // if (json.errcode === 0) { + // message.success("保存成功"); + // }else{ + // message.error("保存失败") + // } + // }) + // } + const getParentKey = (key, tree) => { let parentKey; for (let i = 0; i < tree.length; i++) { @@ -257,9 +422,6 @@ function Detail() { setCurrentQuotationRecord(record); }; - const cancel = () => { - setEditingid(''); - }; const handleDelete = (index) => { const newData = [...quotation]; @@ -295,6 +457,8 @@ function Detail() { tempKey: Math.random() }; setQuotation([...quotation, newData]); + const index = [...quotation, newData].length - 1 + edit(newData, index); }; const handleBatchImport = () => { @@ -376,22 +540,38 @@ function Detail() { { 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 ( - edit(record, index)} style={{ marginRight: 8 }}>{t('Edit')} - handleDelete(index)}> - {t('Delete')} - + {canEdit ? ( + edit(record, index)} style={{ marginRight: 8 }}>{t('Edit')} + ) : ( + {t('Edit')} + )} + {canEdit ? ( + handleDelete(index)}> + {t('Delete')} + + ) : ( + {t('Delete')} + )} - ) - }, - }, + ); + } + }); + } + + + - ]; const handleTagClick = (tag) => { @@ -451,10 +631,10 @@ function Detail() { // 更新整个 lgc_details 对象 const updatedLgcDetails = { ...lgc_details, - [languageStatus]: { - ...lgc_details[languageStatus], - [field]: value, - lgc: languageStatus.toString(), + [languageStatus]: { + ...lgc_details[languageStatus], + [field]: value, + lgc: languageStatus.toString(), id: lgc_details[languageStatus]?.id || '' } }; @@ -477,9 +657,9 @@ function Detail() { //树组件方法 const handleNodeSelect = (_, { node }) => { + console.log("node",node) setSelectedNodeid(node.key); const fatherKey = node.key.split('-')[0]; - setSelectedNodeKey(fatherKey); setSelectedCategory(productProject[fatherKey]); setTags([languageLabel]); // 如果点击的是同一个节点,不做任何操作 @@ -490,23 +670,20 @@ function Detail() { setLanguageLabel(languageLabelRefresh); setSelectedTag(languageLabelRefresh); setRemainderLanguage(HTLanguageSets.filter(item => item.key !== language.toString())); - setEditingProduct(node._raw); + // setEditingProduct(node._raw); if (!node._raw.info.id) { let infoData = node._raw.info; setInfo(node._raw.info); - console.log("node._raw",node._raw) - console.log("没有id"); const newLgcDetails = node._raw.lgc_details const fatherKey = node.key.split('-')[0]; setSelectedNodeid(node.key); - setSelectedNodeKey(fatherKey); form.setFieldsValue({ info: { id: infoData.id || "", title: infoData.title || "", code: infoData.code || "", type: infoData.product_type_id || "", - audit_state:"-1", + audit_state: "-1", create_date: infoData.create_date || "", created_by: infoData.created_by || "", travel_agency_id: travel_agency_id || "", @@ -524,7 +701,7 @@ function Detail() { city_id: infoData.city_id || "", // product_type_name: infoData.product_type_name || "", // dept_name: infoData.dept_name || "", - + }, lgc_details: { lgc: language, @@ -548,9 +725,6 @@ function Detail() { }]) return } else { - - - let initialQuotationData = null; let infoData = null; let lgcDetailsData = null; @@ -576,7 +750,7 @@ function Detail() { }; delete updatedObject.unit_name; delete updatedObject.unit_id; - delete updatedObject.audit_state_id; + // delete updatedObject.audit_state_id; delete updatedObject.audit_state_name; return updatedObject; }); @@ -593,9 +767,6 @@ function Detail() { }); } - if (node._raw.info.id) { - setInfoDataForId(infoData.id) - } setLgc_details(newLgcDetails); @@ -651,12 +822,11 @@ function Detail() { } }); } - - } }; + //新增产品 const handelAddProduct = () => { // 找到对应的产品类型节点 const productTypeNode = treeData.find(item => item.key === addProductType); @@ -711,8 +881,7 @@ function Detail() { return item; }); // 更新 treeData - setEditingProduct(null) - console.log("newTreeData", newTreeData); + // setEditingProduct(null); setTreeData(newTreeData); let tempProductDataList = productsData[addProductType]; @@ -725,62 +894,39 @@ function Detail() { setAddProductVisible(false); } - // let tempProductDataList = productsData[addProductType]; - - // //初始化产品数据 - // const newProduct = { - // info: { - // code: 'addProduct' - // }, - // quotation: [], - // lgc_details: [] - // } - // tempProductDataList.push(newProduct); - // const newProductsData = { - // ...productsData, // 假设使用了展开运算符来复制现有数组 - // [addProductType]: tempProductDataList - // }; - // setProductsData(newProductsData); - // setAddProductVisible(false); - // }; - - const renderFormItem = (item) => { switch (item.code) { case "duration": - return ; + return ; case "display_to_c": return ( - 在计划显示,不在报价信显示 计划和报价信都要显示 计划和报价信都不用显示 ); case "dept_name": - return ; + return ; case "city_name": - return ; + return ; default: - return ; + return ; } }; + //保存产品 const onSave = async (values) => { let tempInfo - console.log("values", values) - console.log("info", info) if (info.id === "") { tempInfo = { ...info, ...values.info, city_name: values.info.city_name.label, - audit_state:"-1" + audit_state: "-1" } delete tempInfo.product_type_name; delete tempInfo.dept_name; - - console.log("新增") let tempQuotation = quotation.map(element => { const updateData = { @@ -790,18 +936,17 @@ function Detail() { delete updateData.tempKey return updateData }) - let tempLgc_details = [{...lgc_details}] - console.log("tempLgc_details",tempLgc_details) + let tempLgc_details = [{ ...lgc_details }] const tempData = { travel_agency_id, info: tempInfo, quotation: tempQuotation, lgc_details: Object.values(lgc_details) }; - console.log("tempData",tempData); + console.log("tempData", tempData); const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/agency_product_save`, tempData); - console.log("result",result) + console.log("result", result); if (errcode === 0) { message.success("保存成功"); setDataFetched(false); @@ -809,14 +954,18 @@ function Detail() { message.error(`保存失败: ${result}`); } return - } else { - tempInfo = { - ...info, - ...values.info, - audit_state: "-1" - } } + + + + tempInfo = { + ...info, + ...values.info, + audit_state: "-1" + } + + console.log("tempInfo", tempInfo) let tempQuotation = quotation.map(element => { @@ -836,8 +985,8 @@ function Detail() { }; 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); @@ -937,12 +1086,14 @@ function Detail() { handleChange('title', e.target.value)} + disabled={isCanEditable} /> handleChange('description', e.target.value)} + disabled={isCanEditable} /> {/* */} @@ -956,145 +1107,159 @@ function Detail() { dataSource={quotation} columns={columns} rowClassName="editable-row" - pagination={{ onChange: cancel }} + // pagination={{ onChange: cancel }} /> - - - + } + + { + !isCanEditable && + + } - + */} {/* */} {/* */} - } onClick={() => setAddProductVisible(true)} /> - - - { - ( - setBatchImportPriceVisible(false)} - width="80%" - > - - - ) - } + {/* } onClick={() => setAddProductVisible(true)} /> */} - { - ( - setAddProductVisible(false)} + } > -

选择产品类别

- - - -

新增产品名称

- setAddProductName(e.target.value)} - /> -
- ) - } + + } onClick={() => setAddProductVisible(true)} /> + + + } 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); + console.log("result", result); + }} /> + + + + - { - ( - -

成人价

- setCurrentQuotationRecord({ ...currentQuotationRecord, adult_cost: e })} /> -

儿童价

- setCurrentQuotationRecord({ ...currentQuotationRecord, child_cost: e })} /> -

币种

- -

类型

- - -

人等

- - setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_min: e })} - style={{ width: '50%', marginRight: '10px' }} - /> - - - setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_max: e })} - style={{ width: '50%', marginLeft: '10px' }} - /> - -

有效期

- { - setCurrentQuotationRecord({ - ...currentQuotationRecord, - use_dates_start: dates[0], - use_dates_end: dates[1] - }); - }} - /> -

周末

- {days.map((day, index) => ( - - ))} + setBatchImportPriceVisible(false)} + width="80%" + > + + + + setAddProductVisible(false)} + > +

选择产品类别

+ + -
+

新增产品名称

+ setAddProductName(e.target.value)} + /> +
+ + +

成人价

+ setCurrentQuotationRecord({ ...currentQuotationRecord, adult_cost: e })} /> +

儿童价

+ setCurrentQuotationRecord({ ...currentQuotationRecord, child_cost: e })} /> +

币种

+ +

类型

+ - ) - } +

人等

+ + setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_min: e })} + style={{ width: '50%', marginRight: '10px' }} + /> + - + setCurrentQuotationRecord({ ...currentQuotationRecord, group_size_max: e })} + style={{ width: '50%', marginLeft: '10px' }} + /> + +

有效期

+ { + setCurrentQuotationRecord({ + ...currentQuotationRecord, + use_dates_start: dates[0], + use_dates_end: dates[1] + }); + }} + /> +

周末

+ {days.map((day, index) => ( + + ))} +
); From d9c45f9962a8bf47c06abd97c46cb6b2ced0d298 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 19 Jul 2024 17:39:03 +0800 Subject: [PATCH 04/12] =?UTF-8?q?feat:=20=E4=BA=A7=E5=93=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86:=20=E5=AE=A1=E6=A0=B8:=20=E5=88=87=E6=8D=A2=E7=8A=B6?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/Products/Index.js | 2 +- src/views/products/Audit.jsx | 32 +++++++++++++++++++--- src/views/products/Detail/CopyProducts.jsx | 11 ++------ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/stores/Products/Index.js b/src/stores/Products/Index.js index 3d727be..365ea7a 100644 --- a/src/stores/Products/Index.js +++ b/src/stores/Products/Index.js @@ -30,7 +30,7 @@ export const copyAgencyDataAction = async (postbody) => { }; export const getAgencyProductsAction = async (param) => { - const _param = { ...param, use_year: String(param.use_year || '').replace('all', ''), audit_state: (param.audit_state || '').replace('all', '') }; + const _param = { ...param, use_year: String(param.use_year || '').replace('all', ''), audit_state: String(param.audit_state || '').replace('all', '') }; const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, _param); return errcode !== 0 ? { agency: {}, products: [] } : result; }; diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index dc35d85..35fd4e3 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -12,11 +12,13 @@ import RequireAuth from '@/components/RequireAuth'; import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; 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 { t } = useTranslation(); const isPermitted = useAuthStore(state => state.isPermitted); const [activeAgency, setActiveAgency] = useProductsStore((state) => [state.activeAgency, state.setActiveAgency]); + const stateMapVal = useProductsAuditStatesMapVal(); const { message, notification } = App.useApp(); const navigate = useNavigate(); @@ -29,6 +31,8 @@ const Header = ({ refresh, ...props }) => { 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 [pickAuditState, setPickAuditState] = useState(); useEffect(() => { refresh(param); navigate(`/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/audit`); @@ -36,12 +40,28 @@ const Header = ({ refresh, ...props }) => { return () => {}; }, [param]); + const emptyPickState = { value: '', label: t('products:State') }; + useEffect(() => { + const baseState = audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`]; + if (isEmpty(pickAuditState)) { + setPickAuditState(baseState); + } + return () => {}; + }, [audit_state, stateMapVal]) + + const handleYearChange = (value) => { setPickYear(value); setParam((pre) => ({ ...pre, ...{ pick_year: value } })); }; + const handleAuditStateChange = (labelValue) => { + const { value } = labelValue || emptyPickState; + setPickAuditState(labelValue || emptyPickState); + setParam((pre) => ({ ...pre, ...{ pick_state: value } })); + }; const handleAgencyChange = ({ label, value }) => { + setPickAgency({ label, value }); setActiveAgency({ travel_agency_id: value, travel_agency_name: label }); setParam((pre) => ({ ...pre, ...{ pick_agency: value } })); }; @@ -71,8 +91,9 @@ const Header = ({ refresh, ...props }) => {

{isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? ( { )} + - { - !isCanEditable && - - } - + { + !isCanEditable && + + } + { !isCanEditable && @@ -1125,9 +1155,6 @@ function Detail() { - {/* */} @@ -1153,6 +1180,10 @@ function Detail() { 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); }} /> From e6bc9b2dc299da7751659ad4e21b0d899be3588c Mon Sep 17 00:00:00 2001 From: Jimmy Liow Date: Tue, 23 Jul 2024 13:59:35 +0800 Subject: [PATCH 09/12] fix: ESLint warning --- src/views/reservation/Detail.jsx | 12 +++++++----- src/views/reservation/Newest.jsx | 5 ++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/views/reservation/Detail.jsx b/src/views/reservation/Detail.jsx index e2cd7c2..e482676 100644 --- a/src/views/reservation/Detail.jsx +++ b/src/views/reservation/Detail.jsx @@ -42,7 +42,7 @@ function Detail() { }, ]; - function detailTextRender(text, confirm) { + function detailTextRender(_, confirm) { const formattedText = confirm.PCI_ConfirmText;//.replace(/\;/g, '\n——————————————————————\n'); return (
@@ -51,19 +51,21 @@ function Detail() { ); } - function attachmentRender(text, confirm) { + function attachmentRender(_, confirm) { return ( <> {confirm.attachmentList.map(attch => { return ( - }>{attch.file_name} + }> + {attch.file_name} + )} )} ); } - function confirmRender(text, confirm) { + function confirmRender(_, confirm) { return ( ); @@ -128,7 +130,7 @@ function Detail() { .finally(() => { setDataLoading(false); }); - }, [reservationId]); + }, [reservationId, getReservationDetail, notification]); return ( <> diff --git a/src/views/reservation/Newest.jsx b/src/views/reservation/Newest.jsx index c6e4279..349d40e 100644 --- a/src/views/reservation/Newest.jsx +++ b/src/views/reservation/Newest.jsx @@ -1,4 +1,4 @@ -import { NavLink, useLocation } from 'react-router-dom' +import { NavLink } from 'react-router-dom' import { useState, useEffect } from 'react' import { Row, Col, Space, Button, Table, Typography, Modal, App, Select } from 'antd' import dayjs from 'dayjs' @@ -90,7 +90,6 @@ function Newest() { ) } - const location = useLocation() const [isModalOpen, setIsModalOpen] = useState(false) const [dataLoading, setDataLoading] = useState(false) const [guideSelectOptions, setGuideSelectOptions] = useState([]) @@ -113,7 +112,7 @@ function Newest() { }) setGuideSelectOptions(selectOptions) }) - }, []) + }, [fetchAllGuideList]) const showCityGuideModal = (reservation) => { setDataLoading(true) From 71147a728d5e49e8d55bc162601e5e7c42ebb162 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 23 Jul 2024 16:14:35 +0800 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86:=20?= =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E9=A1=B5=E9=9D=A2:=20=E8=B0=83=E6=95=B4Heade?= =?UTF-8?q?r=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Audit.jsx | 133 +------------------------- src/views/products/Detail/Header.jsx | 137 +++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 130 deletions(-) create mode 100644 src/views/products/Detail/Header.jsx diff --git a/src/views/products/Audit.jsx b/src/views/products/Audit.jsx index f426b6a..8026a9c 100644 --- a/src/views/products/Audit.jsx +++ b/src/views/products/Audit.jsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { useParams, Link, Navigate, useNavigate } from 'react-router-dom'; -import { App, Empty, Button, Collapse, Table, Space, Divider, Select } from 'antd'; +import { useParams, Link } from 'react-router-dom'; +import { App, Empty, Button, Collapse, Table, Space } from 'antd'; import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets'; import SecondHeaderWrapper from '@/components/SecondHeaderWrapper'; import { useTranslation } from 'react-i18next'; @@ -8,135 +8,8 @@ import useProductsStore, { postProductsQuoteAuditAction, } from '@/stores/Produc import { cloneDeep, isEmpty } from '@/utils/commons'; import useAuthStore from '@/stores/Auth'; import RequireAuth from '@/components/RequireAuth'; -// import PrintContractPDF from './PrintContractPDF'; import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; -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 { t } = useTranslation(); - const isPermitted = useAuthStore(state => state.isPermitted); - const [activeAgency, setActiveAgency] = useProductsStore((state) => [state.activeAgency, state.setActiveAgency]); - const stateMapVal = useProductsAuditStatesMapVal(); - const { message, notification } = App.useApp(); - const navigate = useNavigate(); - - 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 [pickAgency, setPickAgency] = useState({value: activeAgency.travel_agency_id, label: activeAgency.travel_agency_name }); - const [pickAuditState, setPickAuditState] = useState(); - useEffect(() => { - refresh(param); - navigate(`/products/${activeAgency.travel_agency_id}/${pickYear}/${audit_state}/audit`); - - return () => {}; - }, [param]); - - const emptyPickState = { value: '', label: t('products:State') }; - useEffect(() => { - const baseState = audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`]; - if (isEmpty(pickAuditState)) { - setPickAuditState(baseState); - } - return () => {}; - }, [audit_state, stateMapVal]) - - - const handleYearChange = (value) => { - setPickYear(value); - setParam((pre) => ({ ...pre, ...{ pick_year: value } })); - }; - const handleAuditStateChange = (labelValue) => { - const { value } = labelValue || emptyPickState; - setPickAuditState(labelValue || emptyPickState); - setParam((pre) => ({ ...pre, ...{ pick_state: value } })); - }; - - const handleAgencyChange = ({ label, value }) => { - setPickAgency({ label, value }); - setActiveAgency({ travel_agency_id: value, travel_agency_name: label }); - setParam((pre) => ({ ...pre, ...{ pick_agency: value } })); - }; - - const handleAuditItem = (state, row) => { - postProductsQuoteAuditAction(state, { id: row.id, travel_agency_id: activeAgency.travel_agency_id }) - .then((json) => { - if (json.errcode === 0) { - message.success(json.errmsg); - if (typeof refresh === 'function') { - refresh(param); - } - } - }) - .catch((ex) => { - notification.error({ - message: 'Notification', - description: ex.message, - placement: 'top', - duration: 4, - }); - }); - }; - return ( -
-
-

- {isPermitted(PERM_PRODUCTS_OFFER_AUDIT) ? ( - - ) : ( - activeAgency.travel_agency_name - )} - - + + + {/* + {(use_year || '').replace('all', '')} */} +

+
+ {/* */} + {/* */} + + + {t('Edit')} + + + + + + {/* */} + + + + {/* todo: export, 审核完成之后才能导出 */} + + {/* */} +
+ ); +}; +export default Header; From 01e4dbbf4f10eedc5ddf3c3a0902999b08ceb0dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=96=87=E5=BC=BA=40HWQ-PC?= Date: Wed, 24 Jul 2024 10:08:59 +0800 Subject: [PATCH 11/12] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Header=EF=BC=8C?= =?UTF-8?q?=E6=8A=8A=E6=8F=90=E4=BA=A4=E5=AE=A1=E6=A0=B8=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E5=92=8C=E5=A2=9E=E5=8A=A0=E4=BA=A7=E5=93=81=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E7=A7=BB=E5=88=B0Header?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/products/Detail.jsx | 67 ++++++++++++------ src/views/products/Detail/YearSelector.jsx | 81 ++++++++++++++++++++++ 2 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 src/views/products/Detail/YearSelector.jsx diff --git a/src/views/products/Detail.jsx b/src/views/products/Detail.jsx index 145218e..af0ee47 100644 --- a/src/views/products/Detail.jsx +++ b/src/views/products/Detail.jsx @@ -1,9 +1,13 @@ 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 } from 'antd'; +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 { Link, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useProductsTypes, useProductsAuditStatesMapVal } 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'; @@ -79,6 +83,8 @@ function Detail() { getAgencyProducts({ travel_agency_id: agency, use_year: year, audit_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; @@ -1013,29 +1019,47 @@ function Detail() { }; + //提交审核方法 + 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 ( - -
+ + // + }> + {isEmpty(agencyProducts) ? :
- - - - - - - ))} -
-
+
} + + //
); } export default Detail; diff --git a/src/views/products/Detail/YearSelector.jsx b/src/views/products/Detail/YearSelector.jsx new file mode 100644 index 0000000..62e2751 --- /dev/null +++ b/src/views/products/Detail/YearSelector.jsx @@ -0,0 +1,81 @@ +import { useEffect, useState } from 'react'; +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 { isEmpty } from '@/utils/commons'; +import useAuthStore from '@/stores/Auth'; +import RequireAuth from '@/components/RequireAuth'; +// import PrintContractPDF from './PrintContractPDF'; +import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config'; +import dayjs from 'dayjs'; +import AuditStateSelector from '@/components/AuditStateSelector'; +const YearSelector = ({ refresh,onStateChange }) => { + const { travel_agency_id, use_year, audit_state } = useParams(); + const { t } = useTranslation(); + const stateMapVal = useProductsAuditStatesMapVal(); + const navigate = useNavigate(); + + 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 [pickAuditState, setPickAuditState] = useState(); + useEffect(() => { + refresh(param); + + return () => {}; + }, [param]); + + const emptyPickState = { value: '', label: t('products:State') }; + useEffect(() => { + const baseState = audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`]; + if (isEmpty(pickAuditState)) { + setPickAuditState(baseState); + } + return () => {}; + }, [audit_state, stateMapVal]) + + + const handleYearChange = (value) => { + setPickYear(value); + setParam((pre) => ({ ...pre, ...{ pick_year: value } })); + }; + const handleAuditStateChange = (labelValue) => { + const { value } = labelValue || emptyPickState; + setPickAuditState(labelValue || emptyPickState); + setParam((pre) => ({ ...pre, ...{ pick_state: value } })); + }; + const addproducts = () => { + onStateChange('addProducts'); + }; + const submitreview = () => { + onStateChange('submitReview'); + } + return ( +
+
+

+ +