Merge branch 'main' of github.com:hainatravel/GHHub

perf/export-docx
Jimmy Liow 11 months ago
commit 2254a09c5f

@ -28,6 +28,7 @@
"sureCancel": "Are you sure to cancel?",
"sureDelete":"Are you sure to delete?",
"sureSubmit":"Are you sure to submit?",
"Yes": "Yes",
"No": "No",

@ -28,6 +28,7 @@
"sureCancel": "确定取消?",
"sureDelete":"确定删除?",
"sureSubmit":"确定提交?",
"Yes": "是",
"No": "否",

@ -101,7 +101,7 @@ export const useProductsAuditStatesMapVal = (value) => {
export const useProductsTypesFieldsets = (type) => {
const [isPermitted] = useAuthStore((state) => [state.isPermitted]);
const infoDefault = [['city'], ['title']];
const infoAdmin = ['code', 'remarks', 'dept']; // 'display_to_c'
const infoAdmin = ['title', 'code', 'remarks', 'dept']; // 'display_to_c'
const infoDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['display_to_c'] : [];
const infoRecDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['recommends_rate'] : [];
const infoTypesMap = {

@ -113,6 +113,7 @@ const initialState = {
searchValues: {}, // 客服首页: 搜索条件
agencyList: [], // 客服首页: 搜索结果
activeAgency: {}, // 审核/编辑 页: 当前的供应商
activeAgencyState: null,
agencyProducts: {}, // 审核/编辑 页: 供应商产品列表
editingProduct: {}, // 编辑页: 当前编辑的产品
quotationList: [], // 编辑页: 当前产品报价列表
@ -129,6 +130,7 @@ export const useProductsStore = create(
setSearchValues: (searchValues) => set({ searchValues }),
setAgencyList: (agencyList) => set({ agencyList }),
setActiveAgency: (activeAgency) => set({ activeAgency }),
setActiveAgencyState: (activeAgencyState) => set({ activeAgencyState }),
setAgencyProducts: (agencyProducts) => set({ agencyProducts }),
// TODO产品和价格会分开查询编辑
setEditingProduct: (product) => {
@ -214,7 +216,7 @@ export const useProductsStore = create(
},
getAgencyProducts: async (param) => {
const { setLoading, setActiveAgency, setAgencyProducts, editingProduct, setEditingProduct } = get();
const { setLoading, setActiveAgency, setActiveAgencyState, setAgencyProducts, editingProduct, setEditingProduct } = get();
setLoading(true);
setAgencyProducts({});
// setEditingProduct({});
@ -223,6 +225,7 @@ export const useProductsStore = create(
const productsData = groupBy(res.products, (row) => row.info.product_type_id);
setAgencyProducts(productsData);
setActiveAgency(res.agency);
setActiveAgencyState(res.agency.audit_state_id);
if (editingProduct?.info?.id) {
const item = (productsData[editingProduct.info.product_type_id] || []).find((item) => item.info.id === editingProduct.info.id);
setEditingProduct(item);

@ -52,10 +52,11 @@ const PriceTable = ({ productType, dataSource, refresh }) => {
};
const rowStyle = (r, tri) => {
const trCls = tri%2 !== 0 ? ' bg-stone-50' : '';
const trCls = tri%2 !== 0 ? ' bg-stone-50' : ''; //
const [infoI, quoteI] = r.rowSpanI;
const bigTrCls = quoteI === 0 && tri !== 0 ? 'border-collapse border-double border-0 border-t-4 border-stone-300' : '';
return [trCls, bigTrCls].join(' ');
const bigTrCls = quoteI === 0 && tri !== 0 ? 'border-collapse border-double border-0 border-t-4 border-stone-300' : ''; // 线
const editedCls = (r.audit_state_id === 0 && isNotEmpty(r.lastedit_changed)) ? 'bg-red-100' : ''; // , :
return [trCls, bigTrCls, editedCls].join(' ');
};
const columns = [
@ -163,16 +164,26 @@ const TypesPanels = (props) => {
};
const Audit = ({ ...props }) => {
const { notification, modal } = App.useApp()
const isPermitted = useAuthStore(state => state.isPermitted);
const { travel_agency_id, use_year, audit_state } = useParams();
const [loading, activeAgency, getAgencyProducts] = useProductsStore((state) => [state.loading, state.activeAgency, state.getAgencyProducts]);
const [activeAgency, getAgencyProducts] = useProductsStore((state) => [state.activeAgency, state.getAgencyProducts]);
const [loading, setLoading] = useProductsStore(state => [state.loading, state.setLoading]);
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;
getAgencyProducts({ travel_agency_id: agency, use_year: year, audit_state: state });
getAgencyProducts({ travel_agency_id: agency, use_year: year, audit_state: state }).catch(ex => {
setLoading(false);
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
})
});
};
return (

@ -1,53 +1,42 @@
import { useState, useEffect } from 'react';
import { Divider, Empty, Flex } from 'antd';
import { useNavigate } from 'react-router-dom';
import { useState } from 'react';
import { App, Divider, Empty, Flex } from 'antd';
import { isEmpty } from '@/utils/commons';
import SecondHeaderWrapper from '@/components/SecondHeaderWrapper';
import Header from './Detail/Header';
import { useParams } from 'react-router-dom';
import useProductsStore from '@/stores/Products/Index';
import dayjs from 'dayjs';
import { PERM_PRODUCTS_MANAGEMENT } from '@/config';
import { usingStorage } from '@/hooks/usingStorage';
import ProductsTree from './Detail/ProductsTree';
import ProductInfo from './Detail/ProductInfo';
import useAuthStore from '@/stores/Auth';
import NewProductModal from './Detail/NewProductModal';
function Detail() {
const navigate = useNavigate();
const { notification, modal } = App.useApp()
const { travel_agency_id, audit_state, use_year } = useParams();
const [addProductVisible, setAddProductVisible] = useState(false);
const [agencyProducts, loading] = useProductsStore((state) => [state.agencyProducts, state.loading]);
const [agencyProducts, switchParams] = useProductsStore((state) => [state.agencyProducts, state.switchParams]);
const [getAgencyProducts, activeAgency] = useProductsStore((state) => [state.getAgencyProducts, state.activeAgency]);
const [setSwitchParams] = useProductsStore((state) => [state.setSwitchParams]);
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 [loading, setLoading] = useProductsStore(state => [state.loading, state.setLoading]);
const { travelAgencyId } = usingStorage();
const handleGetAgencyProducts = ({ pick_year, pick_agency, pick_state } = {}) => {
const year = pick_year || use_year || dayjs().year();
const year = pick_year || use_year || switchParams.use_year || dayjs().year();
const agency = pick_agency || travel_agency_id || travelAgencyId;
const state = pick_state ?? audit_state;
const param = { travel_agency_id: agency, use_year: year, audit_state: state };
setSwitchParams(param);
// setEditingProduct({});
getAgencyProducts(param);
getAgencyProducts(param).catch(ex => {
setLoading(false);
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
})
});
};
const isPermitted = useAuthStore((state) => state.isPermitted);
const topPerm = isPermitted(PERM_PRODUCTS_MANAGEMENT); //
const [newActionable, setNewActionable] = useState(false);
useEffect(() => {
const notAudit = activeAgency.audit_state_id < 0 || activeAgency.audit_state_id === 3;
setNewActionable(topPerm || notAudit);
return () => {};
}, [activeAgency]);
return (
<SecondHeaderWrapper
loading={loading}
@ -57,7 +46,6 @@ function Detail() {
title={activeAgency.travel_agency_name}
refresh={handleGetAgencyProducts}
handleNewProduct={() => setAddProductVisible(true)}
newActionable={newActionable}
/>
}>
{isEmpty(agencyProducts) ? (

@ -101,13 +101,20 @@ export const CopyProductsFormModal = ({ source, action = '#' | 'o', open, onSubm
const handleCopyAgency = async (param) => {
param.target_agency = isEmpty(param.target_agency) ? source.sourceAgency.travel_agency_id : param.target_agency;
setCopyLoading(true);
console.log(param);
const toID = param.target_agency;
const success = await copyAgencyDataAction({...param, source_agency: source.sourceAgency.travel_agency_id});
// console.log(param);
// const toID = param.target_agency;
const success = await copyAgencyDataAction({...param, source_agency: source.sourceAgency.travel_agency_id}).catch(ex => {
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
})
});
setCopyLoading(false);
success ? message.success(t('Success')) : message.error(t('Failed'));
if (typeof onSubmit === 'function') {
if (success && typeof onSubmit === 'function') {
onSubmit(param);
}
// setCopyModalVisible(false);

@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { useParams, Link, useNavigate, useLocation } from 'react-router-dom';
import { App, Button, Divider, Select } from 'antd';
import { App, Button, Divider, Popconfirm, Select } from 'antd';
import { useProductsAuditStatesMapVal } from '@/hooks/useProductsSets';
import { useTranslation } from 'react-i18next';
import useProductsStore, { postProductsQuoteAuditAction, postAgencyAuditAction } from '@/stores/Products/Index';
import { isEmpty } from '@/utils/commons';
import { isEmpty, objectMapper } from '@/utils/commons';
import useAuthStore from '@/stores/Auth';
import RequireAuth from '@/components/RequireAuth';
// import PrintContractPDF from './PrintContractPDF';
@ -15,19 +15,21 @@ import AuditStateSelector from '@/components/AuditStateSelector';
const Header = ({ refresh, newActionable, ...props }) => {
const location = useLocation();
const isEditPage = location.pathname.includes('edit');
const showEditA = !location.pathname.includes('edit');
const showAuditA = !location.pathname.includes('audit');
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 [switchParams] = useProductsStore((state) => [state.switchParams]);
const [switchParams, setSwitchParams] = useProductsStore((state) => [state.switchParams, state.setSwitchParams]);
const [activeAgencyState] = useProductsStore((state) => [state.activeAgencyState]);
const stateMapVal = useProductsAuditStatesMapVal();
const { message, notification } = App.useApp();
const navigate = useNavigate();
const yearOptions = [];
const currentYear = dayjs().year();
const currentYear = switchParams.use_year || dayjs().year();
const baseYear = use_year ? Number(use_year === 'all' ? currentYear : use_year) : currentYear;
for (let i = currentYear - 5; i <= baseYear + 5; i++) {
yearOptions.push({ label: i, value: i });
@ -38,6 +40,8 @@ const Header = ({ refresh, newActionable, ...props }) => {
const [pickAgency, setPickAgency] = useState({ value: activeAgency.travel_agency_id, label: activeAgency.travel_agency_name });
const [pickAuditState, setPickAuditState] = useState();
useEffect(() => {
const _param = objectMapper(param, { pick_year: 'use_year', pick_agency: 'travel_agency_id', pick_state: 'audit_state' });
setSwitchParams({ ..._param });
refresh(param);
return () => {};
@ -169,19 +173,23 @@ const Header = ({ refresh, newActionable, ...props }) => {
</RequireAuth>
{/* 编辑 */}
<Divider type='vertical' />
{newActionable && (
{isEditPage && (
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Button size='small' type={'primary'} onClick={props.handleNewProduct}>
{t('New')}
{t('products:#')}
</Button>
</RequireAuth>
)}
{activeAgencyState === 0 && (
<>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Button size='small' type={'primary'} onClick={props.handleNewProduct}>
{t('New')}
{t('products:#')}
</Button>
</RequireAuth>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Button size='small' type={'primary'} onClick={handleSubmitForAudit}>
{t('Submit')}
{t('Audit')}
</Button>
<Popconfirm title={t('sureSubmit')} onConfirm={handleSubmitForAudit} okText={t('Yes')} placement={'bottom'}>
<Button size='small' type={'primary'} >
{t('Submit')}
{t('Audit')}
</Button>
</Popconfirm>
</RequireAuth>
</>
)}

@ -58,7 +58,7 @@ export const NewProductModal = ({ source, action = '#' | 'o', open, onSubmit, on
const { t } = useTranslation();
const [formInstance, setFormInstance] = useState();
const [setEditingProduct] = useProductsStore((state) => [state.setEditingProduct]);
const [switchParams, setSwitchParams] = useProductsStore((state) => [state.switchParams, state.setSwitchParams]);
const [switchParams] = useProductsStore((state) => [state.switchParams]);
const [copyLoading, setCopyLoading] = useState(false);
const productsTypesMapVal = useProductsTypesMapVal();

@ -50,17 +50,16 @@ const ProductInfo = ({ ...props }) => {
// "lastedit_changed": "",
};
/** lgc_details */
const copyFields = pick(editingProduct.info, ['title', 'product_type_id']);
const readyToSubInfo = { ...copyNewProduct.info, ...values.info, ...copyFields, type: copyFields.product_type_id, ...poster };
const copyFields = pick(editingProduct.info, ['product_type_id']); // 'title',
const readyToSubInfo = { ...copyNewProduct.info, ...editingProduct.info, ...values.info, ...copyFields, type: copyFields.product_type_id, ...poster };
// console.log('onSave', editingProduct.info, readyToSubInfo);
const prevLgcDetailsMapped = editingProduct.lgc_details.reduce((r, c) => ({ ...r, [c.lgc]: { ...c, description: c.descriptions } }), {}); // todo: description
const mergedLgc = { ...prevLgcDetailsMapped, ...values.lgc_details_mapped };
/** quotation */
// todo:
const prevQuotationMapped = editingProduct.quotation.reduce((r, c) => ({ ...r, [c.id]: { ...c, unit: c.unit_id, audit_state: c.audit_state_id } }), {});
const mergedQ = { ...prevQuotationMapped, ...(values.quotation || []) };
console.log(values);
// console.log(values);
// return false; // debug: 0
/** 提交保存 */
setLoading(true);
@ -69,6 +68,14 @@ const ProductInfo = ({ ...props }) => {
info: readyToSubInfo,
lgc_details: Object.values(mergedLgc),
quotation: Object.values(mergedQ),
}).catch(ex => {
setLoading(false);
notification.error({
message: 'Notification',
description: ex.message,
placement: 'top',
duration: 4,
})
});
setLoading(false);
success ? message.success(t('Success')) : message.error(t('Failed'));

@ -97,7 +97,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
)} */}
</Row>
{/* <Divider className='my-1' /> */}
<Form.Item
<Form.Item className='mb-0'
name={'lgc_details_mapped'}
rules={[
() => ({
@ -110,6 +110,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
.map((x) => HTLanguageSetsMapVal[x.lgc].label)
.join(', ');
if (isNotEmpty(invalidLgcName)) {
// Please complete multi -language information
return Promise.reject(new Error(`请完善多语种信息: ${invalidLgcName}`));
}
return Promise.resolve();
@ -122,9 +123,9 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
<Form.Item hidden name={'id'} label={'ID'}>
<Input />
</Form.Item>
<Form.Item hidden name={'title'} label={'title'}>
{/* <Form.Item hidden name={'title'} label={'title'}>
<Input />
</Form.Item>
</Form.Item> */}
{editable && (
<Form.Item>
<div className='flex justify-around'>
@ -174,6 +175,14 @@ function getFields(props) {
};
let baseChildren = [];
baseChildren = [
item(
'title',
99,
<Form.Item name='title' label={t('Title')} {...fieldProps.title} rules={[{ required: true }]}>
<Input allowClear {...fieldComProps.title} {...styleProps} {...editableProps('title')} />
</Form.Item>,
fieldProps?.title?.col || midCol
),
item(
'code',
99,
@ -400,8 +409,8 @@ const formValuesMapper = (values) => {
}
// omit empty
// Object.keys(dest).forEach((key) => (dest[key] == null || dest[key] === '' || dest[key].length === 0) && delete dest[key]);
const { lgc_details, lgc_details_mapped, ...info } = dest; // quotation
return { info, lgc_details, lgc_details_mapped }; // quotation
const { lgc_details, lgc_details_mapped, quotation, ...info } = dest; // quotation
return { info, lgc_details, lgc_details_mapped, quotation }; // quotation
};
export default InfoForm;

@ -18,7 +18,7 @@ const ProductInfoLgc = ({ editable, formInstance, ...props }) => {
useEffect(() => {
const existsLgc = (editingProduct?.lgc_details || []).map((ele, li) => ({
...ele,
label: HTLanguageSetsMapVal[ele.lgc].label,
label: HTLanguageSetsMapVal[ele.lgc]?.label || ele.lgc,
// key: `${editingProduct.info.id}-${ele.id}`,
key: ele.lgc,
closable: false, // isPermitted(PERM_PRODUCTS_MANAGEMENT) ? true : false,

Loading…
Cancel
Save