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

# Conflicts:
#	src/views/products/Detail/PriceCompactInput.jsx
perf/export-docx
Jimmy Liow 11 months ago
commit 97013bf61f

@ -48,7 +48,7 @@
"OpenHours": "Open Hours",
"Duration": "Duration",
"KM": "KM",
"RecommendsRate": "RecommendsRate",
"RecommendsRate": "Recommends Rate",
"OpenWeekdays": "Open Weekdays",
"DisplayToC": "Display To C",
"Dept": "Dept",
@ -116,20 +116,64 @@
"Weekdays": "Weekdays"
},
"sureSubmitAudit": "确认提交所有产品审核? 提交后,所有产品将进入待审核流程. ",
"FormTooltip": {
"Type": "Product Type",
"Title": "Title",
"Code": "Code",
"City": "City",
"City": "起始城市,举例:北京",
"Dept": "Department",
"Duration": "Duration",
"RecommendsRate": "RecommendsRate",
"Duration": "",
"RecommendsRate": "Recommends Rate",
"OpenHours": "Open Hours",
"OpenWeekdays": "Open Weekdays",
"DisplayToC": "Display Type",
"KM": "KM",
"KM": "往返",
"Description": "Description",
"Remarks": "Memo"
"Remarks": "",
"NewTitle": {
"6": "",
"B": "A点-B点举例桂林-龙胜",
"J": "A点-B点时长车费举例张家界5晚6天车费",
"Q": "城市语种导游工资单位,举例:北京英文导游工资(元/天/团)",
"7": "官方景点名称,举例:陕西历史博物馆",
"R": "普通、豪华、特色餐标,举例:普通餐标",
"8": "举例:故宫导游门票",
"D": "城市A点-B点时间包含内容举例北京市区一日游车导"
},
"----Todo: 下面一组待定": "#",
"6": {
"Title": "",
"#": ""
},
"B": {
"Title": "A点-B点举例桂林-龙胜",
"#": ""
},
"J": {
"Title": "A点-B点时长车费举例张家界5晚6天车费",
"#": ""
},
"Q": {
"Title": "城市语种导游工资单位,举例:北京英文导游工资(元/天/团)",
"#": ""
},
"7": {
"Title": "官方景点名称,举例:陕西历史博物馆",
"#": ""
},
"R": {
"Title": "普通、豪华、特色餐标,举例:普通餐标",
"#": ""
},
"8": {
"Title": "举例:故宫导游门票",
"#": ""
},
"D": {
"Title": "城市A点-B点时间包含内容举例北京市区一日游车导",
"#": ""
}
},
"LgcModal": {

@ -104,20 +104,32 @@
"Weekdays": "周末",
"Operation": "Operation"
},
"FormTooltop": {
"sureSubmitAudit": "确认提交所有产品审核? 提交后,所有产品将进入待审核流程. ",
"FormTooltip": {
"Type": "Product Type",
"Title": "Title",
"Code": "Code",
"City": "City",
"dept": "Department",
"Duration": "Duration",
"RecommendsRate": "RecommendsRate",
"City": "起始城市,举例:北京",
"Dept": "Department",
"Duration": "",
"RecommendsRate": "Recommends Rate",
"OpenHours": "Open Hours",
"OpenWeekdays": "Open Weekdays",
"DisplayToC": "Display Type",
"KM": "KM",
"KM": "往返",
"Description": "Description",
"Remarks": "Remarks"
"Remarks": "",
"NewTitle": {
"6": "",
"B": "A点-B点举例桂林-龙胜",
"J": "A点-B点时长车费举例张家界5晚6天车费",
"Q": "城市语种导游工资单位,举例:北京英文导游工资(元/天/团)",
"7": "官方景点名称,举例:陕西历史博物馆",
"R": "普通、豪华、特色餐标,举例:普通餐标",
"8": "举例:故宫导游门票",
"D": "城市A点-B点时间包含内容举例北京市区一日游车导"
}
},
"LgcModal": {

@ -145,7 +145,7 @@ export const useNewProductRecord = () => {
'dept_id': 0,
'dept_name': '',
'display_to_c': 0,
'km': 0,
'km': undefined,
'city_id': 0,
'city_name': '',
'open_hours': '',

@ -15,6 +15,8 @@ import useNoticeStore from '@/stores/Notice';
import useAuthStore from '@/stores/Auth';
import { useThemeContext } from '@/stores/ThemeContext';
import { usingStorage } from '@/hooks/usingStorage';
import { useDefaultLgc } from '@/i18n/LanguageSwitcher';
import { appendRequestParams } from '@/utils/request'
import { PERM_ACCOUNT_MANAGEMENT, PERM_ROLE_NEW, PERM_OVERSEA, PERM_AIR_TICKET, PERM_PRODUCTS_MANAGEMENT } from '@/config';
@ -63,9 +65,11 @@ function App() {
defaultPath = splitPath[1]
}
const { language } = useDefaultLgc();
const [antdLng, setAntdLng] = useState(enLocale);
useEffect(() => {
setAntdLng(i18n.language === 'en' ? enLocale : zhLocale);
appendRequestParams('lgc', language);
}, [i18n.language])
return (

@ -60,8 +60,6 @@ function Detail() {
<ProductInfo />
</div>
</Flex>
<NewProductModal open={addProductVisible} onSubmit={() => setAddProductVisible(false)} onCancel={() => setAddProductVisible(false)} />
</>
)}
</SecondHeaderWrapper>

@ -12,6 +12,7 @@ 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';
import NewProductModal from './NewProductModal';
const Header = ({ refresh, newActionable, ...props }) => {
const location = useLocation();
@ -174,17 +175,12 @@ const Header = ({ refresh, newActionable, ...props }) => {
{/* 编辑 */}
<Divider type='vertical' />
{isEditPage && (
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Button size='small' type={'primary'} onClick={props.handleNewProduct}>
{t('New')}
{t('products:#')}
</Button>
</RequireAuth>
<NewProductModal />
)}
{activeAgencyState === 0 && (
{/* {activeAgencyState === 0 && ( */}
<>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Popconfirm title={t('sureSubmit')} onConfirm={handleSubmitForAudit} okText={t('Yes')} placement={'bottom'}>
<Popconfirm title={t('products:sureSubmitAudit')} onConfirm={handleSubmitForAudit} okText={t('Yes')} placement={'bottomLeft'}>
<Button size='small' type={'primary'} >
{t('Submit')}
{t('Audit')}
@ -192,7 +188,7 @@ const Header = ({ refresh, newActionable, ...props }) => {
</Popconfirm>
</RequireAuth>
</>
)}
{/* )} */}
</div>
);
};

@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { Form, Modal, Input } from 'antd';
import { Form, Modal, Input, Button } from 'antd';
import { objectMapper } from '@/utils/commons';
import { useTranslation } from 'react-i18next';
@ -7,7 +7,8 @@ import ProductsTypesSelector from '@/components/ProductsTypesSelector';
import useProductsStore from '@/stores/Products/Index';
import { useNewProductRecord, useProductsTypesMapVal } from '@/hooks/useProductsSets';
import { useDefaultLgc } from '@/i18n/LanguageSwitcher';
import dayjs from 'dayjs'
import RequireAuth from '@/components/RequireAuth';
import { PERM_PRODUCTS_OFFER_PUT } from '@/config';
export const NewProductsForm = ({ initialValues, onFormInstanceReady, ...props }) => {
const { t } = useTranslation('products');
@ -17,15 +18,21 @@ export const NewProductsForm = ({ initialValues, onFormInstanceReady, ...props }
onFormInstanceReady(form);
}, []);
const onValuesChange = (changeValues, allValues) => {};
const [pickType, setPickType] = useState({ value: '6' });
const onValuesChange = (changeValues, allValues) => {
if ('products_type' in changeValues) {
setPickType(changeValues.products_type);
}
};
return (
<Form layout='horizontal' form={form} name='new_product_in_modal' initialValues={initialValues} onValuesChange={onValuesChange}>
<Form.Item name={`products_type`} label={t('products:ProductType')} rules={[{ required: true }]} tooltip={t('FormTooltip.Type')}>
<ProductsTypesSelector maxTagCount={1} mode={null} placeholder={t('All')} />
<Form.Item name={`products_type`} label={t('products:ProductType')} rules={[{ required: true }]} tooltip={false}>
<ProductsTypesSelector maxTagCount={1} mode={null} placeholder={t('common:All')} />
</Form.Item>
<Form.Item name={`title`} label={t('products:Title')} rules={[{ required: true }]} tooltip={t('FormTooltip.Title')}>
<Input />
<Form.Item name={`title`} label={t('products:Title')} rules={[{ required: true }]} tooltip={t(`FormTooltip.NewTitle.${pickType.value}`)} dependencies={['products_type']}>
{/* ${pickType.value} */}
<Input placeholder={t(`FormTooltip.NewTitle.${pickType.value}`)} />
</Form.Item>
</Form>
);
@ -54,12 +61,13 @@ const formValuesMapper = (values) => {
/**
*
*/
export const NewProductModal = ({ source, action = '#' | 'o', open, onSubmit, onCancel, initialValues }) => {
export const NewProductModal = ({ initialValues }) => {
const { t } = useTranslation();
const [formInstance, setFormInstance] = useState();
const [setEditingProduct] = useProductsStore((state) => [state.setEditingProduct]);
const [switchParams] = useProductsStore((state) => [state.switchParams]);
const [open, setOpen] = useState(false);
const [copyLoading, setCopyLoading] = useState(false);
const productsTypesMapVal = useProductsTypesMapVal();
const newProduct = useNewProductRecord();
@ -71,26 +79,35 @@ export const NewProductModal = ({ source, action = '#' | 'o', open, onSubmit, on
copyNewProduct.info.product_type_name = productsTypesMapVal[param.products_type.value].label;
copyNewProduct.lgc_details[0].lgc = language;
copyNewProduct.lgc_details[0].title = param.title;
copyNewProduct.quotation[0].use_dates_start = (`${switchParams.use_year}-01-01`);
copyNewProduct.quotation[0].use_dates_end = (`${switchParams.use_year}-12-31`);
copyNewProduct.quotation[0].use_dates_start = `${switchParams.use_year}-01-01`;
copyNewProduct.quotation[0].use_dates_end = `${switchParams.use_year}-12-31`;
setEditingProduct(copyNewProduct);
if (typeof onSubmit === 'function') {
onSubmit();
}
// if (typeof onSubmit === 'function') {
// onSubmit();
// }
setOpen(false);
return false;
};
return (
<>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Button size='small' type={'primary'} onClick={() => setOpen(true)}>
{t('New')}
{t('products:#')}
</Button>
</RequireAuth>
<Modal
width={600}
open={open}
title={`${t('New')}${t('products:#')}`}
title={`${t('common:New')}${t('products:#')}`}
okButtonProps={{
autoFocus: true,
}}
confirmLoading={copyLoading}
onCancel={() => {
onCancel();
// onCancel();
setOpen(false);
formInstance?.resetFields();
}}
destroyOnClose
@ -105,8 +122,6 @@ export const NewProductModal = ({ source, action = '#' | 'o', open, onSubmit, on
}
}}>
<NewProductsForm
action={action}
source={source}
initialValues={initialValues}
onFormInstanceReady={(instance) => {
setFormInstance(instance);

@ -106,4 +106,8 @@ const PriceCompactInput = (props) => {
)
}
<<<<<<< HEAD
export default PriceCompactInput
=======
export default PriceCompactInput
>>>>>>> acaf5a3de7b7197af62517e06d7fdbb7a4a48ace

@ -9,6 +9,7 @@ import { isEmpty, pick } from '@/utils/commons';
import ProductInfoForm from './ProductInfoForm';
import { usingStorage } from '@/hooks/usingStorage';
import Extras from './Extras';
import NewProductModal from './NewProductModal';
const ProductInfo = ({ ...props }) => {
const { t } = useTranslation();
@ -22,18 +23,25 @@ const ProductInfo = ({ ...props }) => {
const [activeAgency, editingProduct, setEditingProduct] = useProductsStore((state) => [state.activeAgency, state.editingProduct, state.setEditingProduct]);
const [extrasVisible, setExtrasVisible] = useState(false);
const [editable, setEditable] = useState(false);
const [editablePerm, setEditablePerm] = useState(false);
const [infoEditable, setInfoEditable] = useState(false);
const [priceEditable, setPriceEditable] = useState(false);
const topPerm = isPermitted(PERM_PRODUCTS_MANAGEMENT); //
useEffect(() => {
const hasHT = (editingProduct?.info?.htid || 0) > 0;
// const hasAuditPer = isPermitted(PERM_PRODUCTS_OFFER_AUDIT);
const hasEditPer = isPermitted(PERM_PRODUCTS_OFFER_PUT);
setEditable(topPerm || (!hasHT && hasEditPer));
setEditablePerm(topPerm || hasEditPer);
// setEditable(topPerm || (hasAuditPer ? true : (!hasHT && hasEditPer)));
// setEditable(true); // debug: 0
// console.log('editable', hasAuditPer, (notAudit && hasEditPer));
setInfoEditable(topPerm || (!hasHT && hasEditPer));
const _priceEditable = [-1, 3].includes(activeAgency?.audit_state_id) || isEmpty(editingProduct?.info?.id);
setPriceEditable(topPerm || (_priceEditable && hasEditPer));
const showExtras = topPerm && !isEmpty(editingProduct) && hasHT;
const showExtras = topPerm && hasHT; // !isEmpty(editingProduct) &&
setExtrasVisible(showExtras);
return () => {};
}, [activeAgency, editingProduct]);
@ -42,18 +50,19 @@ const ProductInfo = ({ ...props }) => {
values.travel_agency_id = activeAgency.travel_agency_id;
const copyNewProduct = structuredClone(newProductRecord);
const poster = {
'audit_state': '-1',
...(topPerm ? {} : { 'audit_state': -1 }), // :
// "create_date": dayjs().format('YYYY-MM-DD HH:mm:ss'),
// "created_by": userId,
'travel_agency_id': activeAgency.travel_agency_id,
// "travel_agency_name": "",
// "lastedit_changed": "",
};
/** lgc_details */
const copyFields = pick(editingProduct.info, ['product_type_id']); // 'title',
const readyToSubInfo = { ...copyNewProduct.info, ...editingProduct.info, ...values.info, ...copyFields, type: copyFields.product_type_id, ...poster };
readyToSubInfo.dept = Number(readyToSubInfo.dept);
// console.log('onSave', editingProduct.info, readyToSubInfo);
const prevLgcDetailsMapped = editingProduct.lgc_details.reduce((r, c) => ({ ...r, [c.lgc]: { ...c, description: c.descriptions } }), {}); // todo: description
/** lgc_details */
const prevLgcDetailsMapped = editingProduct.lgc_details.reduce((r, c) => ({ ...r, [c.lgc]: { ...c, description: c.descriptions } }), {});
const mergedLgc = { ...prevLgcDetailsMapped, ...values.lgc_details_mapped };
// console.log(values);
@ -64,15 +73,15 @@ const ProductInfo = ({ ...props }) => {
travel_agency_id: activeAgency.travel_agency_id,
info: readyToSubInfo,
lgc_details: Object.values(mergedLgc),
quotation: values.quotation, // || editingProduct.quotation, // ,
}).catch(ex => {
quotation: values.quotation.map((q) => ({ ...q, unit: Number(q.unit || q.unit_id), unit_id: Number(q.unit_id) })), // || editingProduct.quotation, // ,
}).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'));
@ -90,10 +99,18 @@ const ProductInfo = ({ ...props }) => {
]}
/>
<Divider className='my-1' />
<h2>{t('products:EditComponents.info')}</h2>
<ProductInfoForm editable={editable} initialValues={editingProduct?.info} onSubmit={onSave} />
<Divider className='my-1' />
{extrasVisible && <Extras productId={editingProduct?.info?.id} />}
{isEmpty(editingProduct) ? (
<div className=' my-2'>
<NewProductModal />
</div>
) : (
<>
<h2>{t('products:EditComponents.info')}</h2>
<ProductInfoForm {...{ editablePerm, infoEditable, priceEditable }} initialValues={editingProduct?.info} onSubmit={onSave} />
<Divider className='my-1' />
{extrasVisible && <Extras productId={editingProduct?.info?.id} />}
</>
)}
</>
);
};

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { Form, Input, Row, Col, Select, Button, InputNumber, Checkbox } from 'antd';
import { App, Form, Input, Row, Col, Select, Button, InputNumber, Checkbox } from 'antd';
import { objectMapper, isEmpty, isNotEmpty } from '@/utils/commons';
import { useTranslation } from 'react-i18next';
import { useWeekdays } from '@/hooks/useDatePresets';
@ -11,7 +11,8 @@ import ProductInfoLgc from './ProductInfoLgc';
import ProductInfoQuotation from './ProductInfoQuotation';
import { useHTLanguageSetsMapVal } from '@/hooks/useHTLanguageSets';
const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, showSubmit, confirmText, formName, ...props }) => {
const InfoForm = ({ onSubmit, onReset, onValuesChange, editablePerm, infoEditable, priceEditable, showSubmit, confirmText, formName, ...props }) => {
const { notification } = App.useApp();
const { t } = useTranslation('products');
const HTLanguageSetsMapVal = useHTLanguageSetsMapVal();
const [loading, editingProduct] = useProductsStore((state) => [state.loading, state.editingProduct]);
@ -28,19 +29,24 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
const filedsets = useProductsTypesFieldsets(editingProduct?.info?.product_type_id);
const shows = filedsets[0];
const [editable, setEditable] = useState(true);
// const [editable, setEditable] = useState(true);
const [formEditable, setFormEditable] = useState(true);
const [showSave, setShowSave] = useState(true);
useEffect(() => {
form.resetFields();
form.setFieldValue('city', { value: editingProduct?.info?.city_id, label: editingProduct?.info?.city_name });
form.setFieldValue('city', editingProduct?.info?.city_id ? { value: editingProduct?.info?.city_id, label: editingProduct?.info?.city_name } : undefined);
form.setFieldValue('dept', { value: editingProduct?.info?.dept_id, label: editingProduct?.info?.dept_name });
const lgc_details_mapped = (editingProduct?.lgc_details || []).reduce((r, c) => ({ ...r, [c.lgc]: c }), {});
form.setFieldValue('lgc_details_mapped', lgc_details_mapped);
form.setFieldValue('quotation', editingProduct?.quotation);
const editable0 = isEmpty(editingProduct) ? false : _editable; //
setEditable(editable0);
setFormEditable(infoEditable || priceEditable);
// const editable0 = isEmpty(editingProduct) ? false : editablePerm; //
setShowSave(infoEditable || priceEditable);
// setEditable(editable0);
return () => {};
}, [editingProduct?.info?.id, _editable]);
}, [editingProduct?.info?.id, editablePerm, infoEditable, priceEditable]);
const onFinish = (values) => {
console.log('Received values of form, origin form value: \n', values);
@ -53,6 +59,12 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
const onFinishFailed = ({ values, errorFields }) => {
console.log('form validate failed', '\nform values:', values, '\nerrorFields', errorFields);
notification.warning({
message: '数据未填写完整',
// description: '',
placement: 'top',
duration: 4,
})
};
const handleReset = () => {
@ -77,16 +89,16 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
<>
<Form
form={form}
disabled={!editable}
disabled={!formEditable}
name={formName || 'product_info'}
// preserve={false}
onFinish={onFinish}
onValuesChange={onIValuesChange}
// onFieldsChange={onFieldsChange}
initialValues={editingProduct?.info}
onFinishFailed={onFinishFailed}>
onFinishFailed={onFinishFailed} scrollToFirstError >
<Row>
{getFields({ sort, initialValue: editingProduct?.info, hides, shows, fieldProps, fieldComProps, form, t, dataSets: { weekdays }, editable })}
{getFields({ sort, initialValue: editingProduct?.info, hides, shows, fieldProps, fieldComProps, form, t, dataSets: { weekdays }, editable: infoEditable })}
{/* {showSubmit && (
<Col flex='1 0 90px' className='flex justify-end items-start'>
<Space align='center'>
@ -118,11 +130,11 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
},
}),
]}>
<ProductInfoLgc editable={editable} formInstance={form} />
<ProductInfoLgc editable={infoEditable} formInstance={form} />
</Form.Item>
<Form.Item name='quotation'>
<ProductInfoQuotation editable={editable} />
<ProductInfoQuotation editable={priceEditable} />
</Form.Item>
<Form.Item hidden name={'id'} label={'ID'}>
@ -131,7 +143,7 @@ const InfoForm = ({ onSubmit, onReset, onValuesChange, editable: _editable, show
{/* <Form.Item hidden name={'title'} label={'title'}>
<Input />
</Form.Item> */}
{editable && (
{showSave && (
<Form.Item>
<div className='flex justify-around'>
<Button type='primary' htmlType='submit' loading={loading}>
@ -196,8 +208,8 @@ function getFields(props) {
item(
'city',
99,
<Form.Item name='city' label={t('City')} {...fieldProps.city} tooltip={t('FormTooltip.City')}>
<CitySelector {...styleProps} {...editableProps('city_id')} />
<Form.Item name='city' label={t('City')} {...fieldProps.city} rules={[{ required: true }]} tooltip={t('FormTooltip.City')}>
<CitySelector {...styleProps} {...editableProps('city_id')} placeholder={t('FormTooltip.City')} />
</Form.Item>,
fieldProps?.city?.col || midCol
),
@ -212,7 +224,7 @@ function getFields(props) {
item(
'duration',
99,
<Form.Item name='duration' label={t('Duration')} {...fieldProps.duration} tooltip={t('FormTooltip.Duration')}>
<Form.Item name='duration' label={t('Duration')} {...fieldProps.duration} rules={[{ required: true, type: 'number', min: 0}]} tooltip={t('FormTooltip.Duration')}>
<InputNumber suffix={'H'} max={24} {...styleProps} {...editableProps('duration')} />
{/* <Input allowClear {...fieldComProps.duration} suffix={'H'} /> */}
</Form.Item>,
@ -221,8 +233,8 @@ function getFields(props) {
item(
'km',
99,
<Form.Item name='km' label={t('KM')} {...fieldProps.km} tooltip={t('FormTooltip.KM')}>
<InputNumber suffix={'KM'} min={0.1} {...styleProps} {...editableProps('km')} />
<Form.Item name='km' label={t('KM')} {...fieldProps.km} rules={[{ required: true, },]} tooltip={t('FormTooltip.KM')}>
<InputNumber suffix={'KM'} min={0.1} {...styleProps} {...editableProps('km')} placeholder={t('FormTooltip.KM')} />
</Form.Item>,
fieldProps?.km?.col || midCol
),
@ -297,7 +309,7 @@ function getFields(props) {
'remarks',
99,
<Form.Item name='remarks' label={t('Remarks')} {...fieldProps.remarks} tooltip={t('FormTooltip.Remarks')}>
<Input.TextArea allowClear rows={2} maxLength={2000} {...fieldComProps.remarks} {...styleProps} {...editableProps('remarks')} />
<Input.TextArea allowClear rows={2} maxLength={2000} showCount {...fieldComProps.remarks} {...styleProps} {...editableProps('remarks')} />
</Form.Item>,
fieldProps?.remarks?.col || 24
),

@ -25,10 +25,11 @@ const ProductInfoLgc = ({ editable, formInstance, ...props }) => {
forceRender: true,
children: (
<Form.Item noStyle>
<Form.Item name={['lgc_details_mapped', `${ele.lgc}`, 'title']} label={t('products:Title')} initialValue={ele.title} rules={[{ required: true }]} tooltip={t('FormTooltip.Title')}>
<Form.Item name={['lgc_details_mapped', `${ele.lgc}`, 'title']} label={t('products:Title')} initialValue={ele.title} rules={[{ required: true }]} tooltip={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)}>
<Input
className={' !text-slate-600'}
allowClear
placeholder={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)}
// onChange={(e) => handleChange('title', e.target.value)}
// disabled={ignoreEditable ? false : (!isEmpty(ele.title) || !editable)}
// disabled={ignoreEditable ? false : !editable}
@ -86,8 +87,8 @@ const ProductInfoLgc = ({ editable, formInstance, ...props }) => {
key: lgcItem.value,
children: (
<Form.Item noStyle>
<Form.Item name={['lgc_details_mapped', `${lgcItem.value}`, 'title']} preserve={false} label={t('products:Title')} rules={[{ required: true }]} tooltip={t('FormTooltip.Title')}>
<Input allowClear />
<Form.Item name={['lgc_details_mapped', `${lgcItem.value}`, 'title']} preserve={false} label={t('products:Title')} rules={[{ required: true }]} tooltip={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)}>
<Input allowClear placeholder={t(`FormTooltip.NewTitle.${editingProduct?.info?.product_type_id}`)} />
</Form.Item>
<Form.Item name={['lgc_details_mapped', `${lgcItem.value}`, 'description']} preserve={false} label={t('products:Description')} tooltip={t('FormTooltip.Description')}>
<Input.TextArea rows={3} allowClear />

@ -229,10 +229,11 @@ const ProductInfoQuotation = ({ editable, ...props }) => {
dataIndex: 'operation',
width: '3%',
render: (_, quotation) => {
const _rowEditable = [-1,3].includes(quotation.audit_state_id);
return (
<Space>
<Button type='link' onClick={() => onQuotationSeleted(quotation)}>{t('Edit')}</Button>
<Button type='link' danger onClick={() => onDeleteQuotation(quotation.id)}>{t('Delete')}</Button>
<Button type='link' disabled={!_rowEditable} onClick={() => onQuotationSeleted(quotation)}>{t('Edit')}</Button>
<Button type='link' danger disabled={!_rowEditable} onClick={() => onDeleteQuotation(quotation.id)}>{t('Delete')}</Button>
</Space>
)
},

@ -4,6 +4,7 @@ import { CaretDownOutlined } from '@ant-design/icons';
import { useTranslation } from 'react-i18next';
import useProductsStore from '@/stores/Products/Index';
import { useProductsTypes, useProductsAuditStatesMapVal } from '@/hooks/useProductsSets';
import { groupBy, sortBy } from '@/utils/commons';
const flattenTreeFun = (tree) => {
let flatList = [];
@ -52,6 +53,13 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
// ;
// const title = text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '';
const hasDataTypes = Object.keys(agencyProducts);
// const cityData = groupBy(productsSortByHT, (row) => `${row.info.city_id}-${row.info.city_name}`);
const copyAgencyProducts = structuredClone(agencyProducts);
Object.keys(copyAgencyProducts).map((key) => {
const _cityProductsData = groupBy(copyAgencyProducts[key], (row) => `${row.info.city_name || '(空)'}`);
copyAgencyProducts[key] = _cityProductsData;
});
// console.log(copyAgencyProducts);
const _show = productsTypes
.filter((kk) => hasDataTypes.includes(kk.value))
.map((ele) => ({
@ -62,10 +70,20 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
const lgc_map = product.lgc_details.reduce((rlgc, clgc) => ({...rlgc, [clgc.lgc]: clgc}), {});
return {
title: product.info.title || lgc_map?.['2']?.title || lgc_map?.['1']?.title || '',
// title: `${product.info.city_name}` + (product.info.title || lgc_map?.['2']?.title || lgc_map?.['1']?.title || ''),
// key: `${ele.value}-${product.info.id}`,
key: product.info.id,
_raw: product,
isLeaf: true,
}}),
// ``
_children: Object.keys(copyAgencyProducts[ele.value] || []).map(city => {
return {
title: city,
key: `${ele.value}-${city}`,
children: copyAgencyProducts[ele.value][city],
};
}),
}));
setTreeData(_show);
setRawTreeData(_show);
@ -88,12 +106,17 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
setSearchValue(value);
setAutoExpandParent(true);
};
const handleNodeSelect = (_, { node }) => {
const handleNodeSelect = (selectedKeys, { node }) => {
if (node._raw) {
setEditingProduct(node._raw);
} else {
// : /
// const isExpand = expandedKeys.includes(selectedKeys[0]);
// const _keys = isExpand ? expandedKeys.filter(k => k !== node.key) : [].concat(expandedKeys, selectedKeys);
// setExpandedKeys(_keys);
}
if (typeof onNodeSelect === 'function') {
onNodeSelect(_, { node });
onNodeSelect(selectedKeys, { node });
}
};
const onExpand = (keys) => {
@ -123,7 +146,7 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
<Input.Search placeholder='Search' onChange={onSearch} allowClear className='sticky top-1 z-20 mb-3' />
<Tree
blockNode
showLine
showLine defaultExpandAll expandAction={'doubleClick'}
selectedKeys={[editingProduct?.info?.id || editingProduct?.info?.product_type_id]}
switcherIcon={<CaretDownOutlined />}
onSelect={handleNodeSelect}

Loading…
Cancel
Save