产品编辑页面: 组件 info, form, lgc

perf/export-docx
Lei OT 1 year ago
parent 2a193f9955
commit a63887a7b6

@ -101,16 +101,17 @@ export const useProductsAuditStatesMapVal = (value) => {
export const useProductsTypesFieldsets = (type) => {
const [isPermitted] = useAuthStore((state) => [state.isPermitted]);
const infoDefault = [['code'], ['title']];
const infoAdmin = ['remarks', 'dept', ]; // 'display_to_c'
const infoAdmin = ['remarks', 'dept']; // 'display_to_c'
const infoDisplay = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? ['display_to_c'] : [];
const infoTypesMap = {
'6': [['city',],[]],
'6': [['city'], []],
'B': [['city', 'km'], []],
'J': [['city', 'recommends_rate', 'duration', 'display_to_c'], ['description',]],
'Q': [['city', 'recommends_rate', 'duration', 'display_to_c'], ['description',]],
'D': [['city', 'recommends_rate','duration', 'display_to_c'], ['description',]],
'7': [['city', 'recommends_rate', 'duration', 'display_to_c', 'open_weekdays'], ['description',]],
'R': [['city',], ['description',]],
'8': [['display_to_c'],[]], // todo: ?
'J': [['city', 'recommends_rate', 'duration', ...infoDisplay], ['description']],
'Q': [['city', 'recommends_rate', 'duration', ...infoDisplay], ['description']],
'D': [['city', 'recommends_rate', 'duration', ...infoDisplay], ['description']],
'7': [['city', 'recommends_rate', 'duration', ...infoDisplay, 'open_weekdays'], ['description']],
'R': [['city'], ['description']],
'8': [[...infoDisplay], []],
};
const thisTypeFieldset = (_type) => {
if (isEmpty(_type)) {
@ -119,8 +120,62 @@ export const useProductsTypesFieldsets = (type) => {
const adminSet = isPermitted(PERM_PRODUCTS_MANAGEMENT) ? infoAdmin : [];
return [
[...infoDefault[0], ...infoTypesMap[_type][0], ...adminSet],
[...infoDefault[1], ...infoTypesMap[_type][1]]
[...infoDefault[1], ...infoTypesMap[_type][1]],
];
};
return thisTypeFieldset(type);
}
};
export const useNewProductRecord = () => {
return {
info: {
'id': null,
'title': '',
'code': '',
'product_type_id': '',
'product_type_name': '',
'remarks': '',
'duration': 0,
'duration_unit': 'h',
'open_weekdays': '',
'recommends_rate': 0,
'dept_id': 0,
'dept_name': '',
'display_to_c': 0,
'km': 0,
'city_id': 0,
'city_name': '',
},
default_lgc: {
'title': '',
'description': '',
'lgc': 1,
'id': null,
},
default_quotation: {
'id': null,
'adult_cost': 0,
'child_cost': 0,
'currency': 'CNY',
'unit_id': '1',
'unit_name': '每团',
'group_size_min': 1,
'group_size_max': 2,
'use_dates_start': '',
'use_dates_end': '',
'weekdays': '',
'audit_state_id': -1,
'audit_state_name': '',
'lastedit_changed': '',
},
lgc_details: [
{
'title': '',
'description': '',
'lgc': 1,
'id': null,
},
],
quotation: [],
};
};

@ -107,6 +107,7 @@ const initialState = {
activeAgency: {}, // 审核/编辑 页: 当前的供应商
agencyProducts: {}, // 审核/编辑 页: 供应商产品列表
editingProduct: {}, // 编辑页: 当前编辑的产品
editing: false,
};
export const useProductsStore = create(
devtools((set, get) => ({
@ -120,6 +121,7 @@ export const useProductsStore = create(
setActiveAgency: (activeAgency) => set({ activeAgency }),
setAgencyProducts: (agencyProducts) => set({ agencyProducts }),
setEditingProduct: (editingProduct) => set({ editingProduct }),
setEditing: (editing) => set({ editing }),
reset: () => set(initialState),

@ -135,7 +135,7 @@ const TypesPanels = (props) => {
.map((w) => t(`weekdaysShort.${w}`))
.join(', '),
info: c.info,
lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}),
lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...rlgc, [clgc.lgc]: clgc}), {}),
rowSpan: i === 0 ? c.quotation.length : 0,
rowSpanI: [ri, i],
}))

@ -2,7 +2,7 @@ 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 } from '@/hooks/useProductsSets';
import { useProductsTypes, useProductsAuditStatesMapVal, useProductsTypesMapVal, useNewProductRecord } from '@/hooks/useProductsSets';
import Extras from './Detail/Extras';
import { isEmpty } from '@/utils/commons';
import SecondHeaderWrapper from '@/components/SecondHeaderWrapper';
@ -860,12 +860,20 @@ function Detail() {
};
//
const handelAddProduct = () => {
const newProduct = useNewProductRecord();
// const { language } = useDefaultLgc();
const handelAddProduct = () => {
//
const productTypeNode = treeData.find(item => item.key === addProductType);
console.log("productTypeNode", productTypeNode)
// if (productTypeNode) {
// // children
// 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: "",
@ -1065,12 +1073,12 @@ function Detail() {
// <YearSelector title={activeAgency.travel_agency_name} refresh={handleGetAgencyProducts} onStateChange={handleStateChange}/>
}>
{isEmpty(agencyProducts) ? <Empty /> :
<div>
<>
<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'>
<div className=' flex-auto grow-0 min-w-[800px]'>
<ProductInfo />
{/* <Row> */}
{/* <Col span={6} className=' relative'> */}
@ -1327,7 +1335,7 @@ function Detail() {
</Button>
))}
</Modal> */}
</div>}
</>}
</SecondHeaderWrapper>
);
}

@ -23,7 +23,7 @@ const Header = ({ refresh, ...props }) => {
const yearOptions = [];
const currentYear = dayjs().year();
const baseYear = Number(use_year === 'all' ? currentYear : use_year);
const baseYear = use_year ? Number(use_year === 'all' ? currentYear : use_year) : currentYear;
for (let i = baseYear - 3; i <= baseYear + 3; i++) {
yearOptions.push({ label: i, value: i });
}
@ -40,7 +40,7 @@ const Header = ({ refresh, ...props }) => {
const emptyPickState = { value: '', label: t('products:State') };
useEffect(() => {
const baseState = audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`];
const baseState = audit_state ? (audit_state === 'all' ? emptyPickState : stateMapVal[`${audit_state}`]) : emptyPickState;
if (isEmpty(pickAuditState)) {
setPickAuditState(baseState);
}

@ -2,193 +2,44 @@ import { createContext, useEffect, useState } from 'react';
import { Breadcrumb, Form, Divider, Button, Input, Select, Row, Col } from 'antd';
import { useTranslation } from 'react-i18next';
import { useProductsTypesMapVal, } from '@/hooks/useProductsSets';
import useProductsStore from '@/stores/Products/Index';
import useProductsStore, { postProductsSave } from '@/stores/Products/Index';
import useAuthStore from '@/stores/Auth';
import RequireAuth from '@/components/RequireAuth';
import { PERM_PRODUCTS_MANAGEMENT, PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config';
import DeptSelector from '@/components/DeptSelector';
import CitySelector from '@/components/CitySelector';
import { at, isEmpty } from '@/utils/commons';
import { at, isEmpty, pick } from '@/utils/commons';
import ProductInfoForm from './ProductInfoForm';
const ProductInfo = ({ ...props }) => {
const { t } = useTranslation();
const [form] = Form.useForm();
const isPermitted = useAuthStore((state) => state.isPermitted);
const productsTypesMapVal = useProductsTypesMapVal();
const [agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.agencyProducts, state.editingProduct, state.setEditingProduct]);
const [activeAgency, agencyProducts, editingProduct, setEditingProduct] = useProductsStore((state) => [state.activeAgency, state.agencyProducts, state.editingProduct, state.setEditingProduct]);
const [editable, setEditable] = useState(true);
useEffect(() => {
const notAudit = activeAgency.audit_state_id < 0 || activeAgency.audit_state_id === 3;
const hasAuditPer = isPermitted(PERM_PRODUCTS_OFFER_AUDIT);
const hasEditPer = isPermitted(PERM_PRODUCTS_OFFER_PUT);
setEditable(notAudit && hasEditPer);
// setEditable(true); // test: 0
return () => {};
}, [activeAgency, editingProduct])
return () => {
}
}, [editingProduct])
const productTypeFormItems = {
'6': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
],
'B': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
{ key: 'km', name: t('products:KM'), nameKey: 'products:KM' },
{
key: 'remarks',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Remarks')}</RequireAuth>,
nameKey: 'products:Remarks',
},
],
'J': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
{
key: 'recommends_rate',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:recommendationRate')}</RequireAuth>,
nameKey: 'products:recommendationRate',
},
{ key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' },
{
key: 'dept_name',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Dept')}</RequireAuth>,
nameKey: 'products:Dept',
},
{
key: 'display_to_c',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:DisplayToC')}</RequireAuth>,
nameKey: 'products:DisplayToC',
},
{
key: 'remarks',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Remarks')}</RequireAuth>,
nameKey: 'products:Remarks',
},
],
'Q': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
{
key: 'recommends_rate',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:recommendationRate')}</RequireAuth>,
nameKey: 'products:recommendationRate',
},
{ key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' },
{
key: 'dept_name',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Dept')}</RequireAuth>,
nameKey: 'products:Dept',
},
{
key: 'display_to_c',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:DisplayToC')}</RequireAuth>,
nameKey: 'products:DisplayToC',
},
{
key: 'remarks',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Remarks')}</RequireAuth>,
nameKey: 'products:Remarks',
},
],
'D': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
{
key: 'recommends_rate',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:recommendationRate')}</RequireAuth>,
nameKey: 'products:recommendationRate',
},
{ key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' },
{
key: 'dept_name',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Dept')}</RequireAuth>,
nameKey: 'products:Dept',
},
{
key: 'display_to_c',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:DisplayToC')}</RequireAuth>,
nameKey: 'products:DisplayToC',
},
{
key: 'remarks',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Remarks')}</RequireAuth>,
nameKey: 'products:Remarks',
},
],
'7': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
{
key: 'recommends_rate',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:recommendationRate')}</RequireAuth>,
nameKey: 'products:recommendationRate',
},
{ key: 'duration', name: t('products:Duration'), nameKey: 'products:Duration' },
{ key: 'open_weekdays', name: t('products:OpenWeekdays'), nameKey: 'products:OpenWeekdays' },
{
key: 'remarks',
name: <RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>{t('products:Remarks')}</RequireAuth>,
nameKey: 'products:Remarks',
},
],
'8': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
],
'R': [
{ key: 'code', name: t('products:Code'), nameKey: 'products:Code' },
{ key: 'city_name', name: t('products:City'), nameKey: 'products:City' },
],
};
const isCanEditable = false;
const renderFormItem = (item) => {
// const item = { key: 'code', name: t('products:Code'), nameKey: 'products:Code' };
// console.log(editingProduct?.info);
if (isEmpty(editingProduct)) {
return null;
}
console.log(item.key, at(editingProduct?.info, item.key)[0]);
let InputCom = <Input disabled={isCanEditable} />;
switch (item.key) {
case 'duration':
InputCom = <Input suffix='H' disabled={isCanEditable} />;
break;
case 'display_to_c':
InputCom = (
<Select disabled={isCanEditable}>
<Select.Option value={0}>在计划显示不在报价信显示</Select.Option>
<Select.Option value={1}>计划和报价信都要显示</Select.Option>
<Select.Option value={2}>计划和报价信都不用显示</Select.Option>
</Select>
);
break;
case 'dept_name':
InputCom = <DeptSelector disabled={isCanEditable} />;
break;
case 'city_name':
InputCom = <CitySelector disabled={isCanEditable} />;
break;
case 'remarks':
InputCom = <Input.TextArea rows={2} disabled={isCanEditable} />;
break;
default:
InputCom = <Input disabled={isCanEditable} />;
}
return (
<Col span={item.key === 'remarks' ? 24 : 8} key={`${item.key}`}>
<Form.Item name={item.key} label={t(item.nameKey)} >
{/* {renderFormItem(item)} */}
<InputCom />
</Form.Item>
</Col>
);
};
const onValuesChange = (changedValues, allValues) => {
// const dest = formValuesMapper(allValues);
console.log('form onValuesChange', Object.keys(changedValues), );
};
const onSave = (e) => {
console.log(e);
const onSave = (err, values, forms) => {
values.travel_agency_id = activeAgency.travel_agency_id;
const poster = {
"audit_state": "-1",
// "create_date": "",
// "created_by": "",
"travel_agency_id": activeAgency.travel_agency_id,
// "travel_agency_name": "",
// "lastedit_changed": "",
};
const copyFields = pick(editingProduct.info, ['title', 'product_type_id', ]);
const readyToSubInfo = {...values.info, ...copyFields, type: copyFields.product_type_id, ...poster };
console.log('onSave', editingProduct.info, readyToSubInfo);
};
return (
<>
@ -200,16 +51,7 @@ const ProductInfo = ({ ...props }) => {
/>
<Divider className='my-1' />
<h2>{t('products:EditComponents.info')}</h2>
<ProductInfoForm editable showSubmit initialValues={editingProduct?.info} />
{/* <Form form={form} name='product-edit' initialValues={editingProduct?.info} onValuesChange={onValuesChange} onFinish={onSave}>
<Form.Item>
<div className='flex justify-around'>
<Button type='primary' htmlType='submit'>
{t('Save')}
</Button>
</div>
</Form.Item>
</Form> */}
<ProductInfoForm editable={editable} showSubmit initialValues={editingProduct?.info} onSubmit={onSave} />
<Divider className='my-1' />
</>
);

@ -1,6 +1,6 @@
import { useEffect } from 'react';
import { Form, Input, Row, Col, Select, DatePicker, Space, Button, InputNumber, Radio, Checkbox, Divider } from 'antd';
import { objectMapper, at } from '@/utils/commons';
import { objectMapper, at, isEmpty } from '@/utils/commons';
import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config';
// import useFormStore from '@/stores/Form';
import { useTranslation } from 'react-i18next';
@ -74,8 +74,8 @@ const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, s
return (
<>
<Form form={form} disabled={!(editable || false)} name={formName || 'product_info'} onFinish={onFinish} onValuesChange={onIValuesChange} initialValues={editingProduct?.info}>
<Row gutter={16}>
{getFields({ sort, initialValue: editingProduct?.info, hides, shows, fieldProps, fieldComProps, form, t, dataSets: { weekdays } })}
<Row>
{getFields({ sort, initialValue: editingProduct?.info, hides, shows, fieldProps, fieldComProps, form, t, dataSets: { weekdays }, editable })}
{/* {showSubmit && (
<Col flex='1 0 90px' className='flex justify-end items-start'>
<Space align='center'>
@ -88,13 +88,13 @@ const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, s
</Row>
{/* <Divider className='my-1' /> */}
<ProductInfoLgc formInstance={form} />
<ProductInfoQuotation formInstance={form} />
<ProductInfoLgc editable={editable} formInstance={form} />
<ProductInfoQuotation editable={editable} formInstance={form} />
<Form.Item hidden name={'id'} label={'ID'}>
<Input />
</Form.Item>
{showSubmit && (
{editable && (
<Form.Item>
<div className='flex justify-around'>
<Button type='primary' htmlType='submit'>
@ -110,18 +110,23 @@ const InfoForm = ({ formInstance, onSubmit, onReset, onValuesChange, editable, s
function getFields(props) {
const { fieldProps, fieldComProps, form, t, dataSets } = props;
console.log('getFields', props.initialValue);
// console.log('getFields', props.initialValue);
const styleProps = {};
// !props.editable ? {
// variant: 'borderless',
// className: '!text-slate-500',
// } : {};
const bigCol = 4 * 2;
const midCol = 6;
const midCol = 8;
const layoutProps = {
// gutter: { xs: 8, sm: 8, lg: 16 },
lg: { span: 4 },
lg: { span: 6 },
md: { span: 8 },
sm: { span: 12 },
xs: { span: 24 },
};
const item = (name, sort = 0, render, col) => {
const customCol = col || 4;
const customCol = col || midCol;
const mdCol = customCol * 2;
return {
'key': '',
@ -129,7 +134,7 @@ function getFields(props) {
name,
render,
'hide': false,
'col': { lg: { span: customCol }, md: { span: mdCol < 8 ? 10 : mdCol }, flex: mdCol < 8 ? '1 0' : '' },
'col': { lg: { span: customCol }, md: { span: mdCol < 8 ? 10 : (mdCol > 24 ? 24 : mdCol) }, flex: mdCol < 8 ? '1 0' : '' },
};
};
let baseChildren = [];
@ -138,49 +143,49 @@ function getFields(props) {
'code',
99,
<Form.Item name='code' label={t('Code')} {...fieldProps.code} _initialValue={at(props, 'initialValue.code')[0]}>
<Input allowClear {...fieldComProps.code} />
<Input allowClear {...fieldComProps.code} {...styleProps} />
</Form.Item>,
fieldProps?.code?.col || 8
fieldProps?.code?.col || midCol
),
item(
'city', // todo:
99,
<Form.Item name='city' label={t('City')} {...fieldProps.city} _initialValue={at(props, 'initialValue.city')[0]}>
<CitySelector />
<CitySelector {...styleProps} />
</Form.Item>,
fieldProps?.city?.col || 8
fieldProps?.city?.col || midCol
),
item(
'dept', // todo:
99,
<Form.Item name='dept' label={t('Dept')} {...fieldProps.dept}>
<DeptSelector labelInValue={false} />
<DeptSelector labelInValue={false} isLeaf {...styleProps} />
</Form.Item>,
fieldProps?.dept?.col || 8
fieldProps?.dept?.col || midCol
),
item(
'duration',
99,
<Form.Item name='duration' label={t('Duration')} {...fieldProps.duration}>
<InputNumber suffix={'H'} max={24} />
<InputNumber suffix={'H'} max={24} {...styleProps} />
{/* <Input allowClear {...fieldComProps.duration} suffix={'H'} /> */}
</Form.Item>,
fieldProps?.duration?.col || 8
fieldProps?.duration?.col || midCol
),
item(
'km',
99,
<Form.Item name='km' label={t('KM')} {...fieldProps.km}>
<InputNumber suffix={'KM'} min={1} />
<InputNumber suffix={'KM'} min={0.1} {...styleProps} />
</Form.Item>,
fieldProps?.km?.col || 8
fieldProps?.km?.col || midCol
),
item(
'recommends_rate', // todo:
99,
<Form.Item name='recommends_rate' label={t('RecommendsRate')} {...fieldProps.recommends_rate}>
{/* <Input placeholder={t('RecommendsRate')} allowClear /> */}
<Select
<Select {...styleProps}
style={{ width: '100%' }}
labelInValue
options={[
@ -192,7 +197,7 @@ function getFields(props) {
]}
/>
</Form.Item>,
fieldProps?.recommends_rate?.col || 8
fieldProps?.recommends_rate?.col || midCol
),
item(
'display_to_c',
@ -222,16 +227,16 @@ function getFields(props) {
{ value: '153001', label: '在计划显示,不在报价信显示' },
{ value: 0, label: '计划和报价信都要显示' },
{ value: '153001, 153002', label: '计划和报价信都不用显示' },
]} />
]} {...styleProps} />
</Form.Item>,
fieldProps?.display_to_c?.col || 8
fieldProps?.display_to_c?.col || midCol
),
item(
'open_weekdays',
99,
<Form.Item name='open_weekdays' label={t('OpenWeekdays')} initialValue={['1', '2', '3', '4', '5', '6', '7']} {...fieldProps.open_weekdays}>
{/* 默认全部 */}
<Checkbox.Group options={dataSets.weekdays} />
<Checkbox.Group options={dataSets.weekdays} {...styleProps} />
</Form.Item>,
fieldProps?.open_weekdays?.col || 24
),
@ -239,7 +244,7 @@ function getFields(props) {
'remarks',
99,
<Form.Item name='remarks' label={t('Remarks')} {...fieldProps.remarks}>
<Input.TextArea allowClear rows={2} maxLength={2000} {...fieldComProps.remarks} />
<Input.TextArea allowClear rows={2} maxLength={2000} {...fieldComProps.remarks} {...styleProps} />
</Form.Item>,
fieldProps?.remarks?.col || 24
),
@ -282,7 +287,7 @@ function getFields(props) {
style = i % 2 === 0 && baseChildren[i].col === 12 ? { ...style, ...leftStyle } : style;
style = !baseChildren[i].hide ? { ...style, display: 'block' } : { ...style, display: 'none' };
const Item = (
<Col key={String(i)} style={style} {...layoutProps} {...baseChildren[i].col}>
<Col key={String(i)} style={style} {...baseChildren[i].col} className='px-1 shrink-0 grow'>
{baseChildren[i].render}
</Col>
);
@ -290,28 +295,32 @@ function getFields(props) {
}
return children;
}
const formValuesMapper = (values) => {
const destinationObject = {
'keyword': { key: 'keyword', transform: (value) => value || '' },
'referenceNo': { key: 'referenceNo', transform: (value) => value || '' },
'dates': [
{ key: 'startdate', transform: (arrVal) => (arrVal ? arrVal[0].format(DATE_FORMAT) : '') },
{ key: 'enddate', transform: (arrVal) => (arrVal ? arrVal[1].format(DATE_FORMAT) : '') },
{ key: 'starttime', transform: (arrVal) => (arrVal ? arrVal[0].format(DATE_FORMAT) : '') },
{ key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') },
],
'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' },
'audit_state': { key: 'audit_state', transform: (value) => value?.value || value?.key || '', default: '' },
'agency': {
key: 'agency',
transform: (value) => {
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : '';
'city': [{ key: 'city_id', transform: (value) => value?.value || value?.key || '' }, { key: 'city_name', transform: (value) => value?.label || '' }],
'dept': { key: 'dept', transform: (value) => typeof value === 'string' ? value : (value?.value || value?.key || '') },
'open_weekdays': { key: 'open_weekdays', transform: (value) => (Array.isArray(value) ? value.join(',') : value) },
'recommends_rate': { key: 'recommends_rate', transform: (value) => typeof value === 'string' ? value : (value?.value || value?.key || '') },
'lgc_details': [
{
key: 'lgc_details',
transform: (value) => {
const _val = value.filter((s) => !isEmpty(s));
return _val || '';
},
},
},
'year': [{ key: 'year', transform: (arrVal) => (arrVal ? arrVal.format('YYYY') : '') }],
{
key: 'lgc_details_mapped',
transform: (value) => {
const _val = value.filter((s) => !isEmpty(s));
return _val.reduce((r, c) => ({ ...r, [c.lgc]: c }), {});
},
},
],
};
let dest = {};
const { dates, ...omittedValue } = values;
const { city, ...omittedValue } = values;
dest = { ...omittedValue, ...objectMapper(values, destinationObject) };
for (const key in dest) {
if (Object.prototype.hasOwnProperty.call(dest, key)) {
@ -320,7 +329,8 @@ const formValuesMapper = (values) => {
}
// omit empty
// Object.keys(dest).forEach((key) => (dest[key] == null || dest[key] === '' || dest[key].length === 0) && delete dest[key]);
return dest;
const { lgc_details, lgc_details_mapped, ...info } = dest;
return { info, lgc_details, lgc_details_mapped };
};

@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next';
import { useDefaultLgc } from '@/i18n/LanguageSwitcher';
import { cloneDeep, isEmpty } from '@/utils/commons';
const ProductInfoLgc = ({ formInstance, ...props }) => {
const ProductInfoLgc = ({ editable, formInstance, ...props }) => {
const { t } = useTranslation();
const { language: languageHT } = useDefaultLgc();
const [isPermitted, currentUser] = useAuthStore((state) => [state.isPermitted, state.currentUser]);
@ -27,31 +27,31 @@ const ProductInfoLgc = ({ formInstance, ...props }) => {
const [items, setItems] = useState([]);
const newTabIndex = useRef(0);
useEffect(() => {
const existsLgc = (editingProduct?.lgc_details || []).map((ele) => ({
const existsLgc = (editingProduct?.lgc_details || []).map((ele, li) => ({
...ele,
label: HTLanguageSetsMapVal[ele.lgc].label,
key: `${editingProduct.info.id}-${ele.id}`,
closable: false, // isPermitted(PERM_PRODUCTS_MANAGEMENT) ? true : false,
children: (
<>
<Form.Item name={[`${ele.lgc}`, 'title']} label={t('products:Title')} initialValue={ele.title}>
<Input
<Form.Item name={[li, 'title']} label={t('products:Title')} initialValue={ele.title}>
<Input className=' !text-slate-500'
// onChange={(e) => handleChange('title', e.target.value)}
// disabled={isCanEditable}
disabled={!isEmpty(ele.title)}
disabled={!isEmpty(ele.title) || !editable}
/>
</Form.Item>
<Form.Item name={[`${ele.lgc}`, 'description']} label={t('products:Description')} initialValue={ele.descriptions}>
<Input.TextArea
<Form.Item name={[li, 'description']} label={t('products:Description')} initialValue={ele.descriptions}>
<Input.TextArea className='!text-slate-500'
rows={3}
// onChange={(e) => handleChange('description', e.target.value)}
disabled={!isEmpty(ele.descriptions)}
disabled={!isEmpty(ele.descriptions) || !editable}
/>
</Form.Item>
<Form.Item hidden name={[`${ele.lgc}`, 'lgc']} initialValue={ele.lgc}>
<Form.Item hidden name={[li, 'lgc']} initialValue={ele.lgc}>
<Input />
</Form.Item>
<Form.Item hidden name={[`${ele.lgc}`, 'id']} initialValue={ele.id}>
<Form.Item hidden name={[li, 'id']} initialValue={ele.id}>
<Input />
</Form.Item>
</>
@ -72,21 +72,22 @@ const ProductInfoLgc = ({ formInstance, ...props }) => {
};
const addLgc = (lgcItem) => {
const newActiveKey = lgcItem.value; // `newTab${newTabIndex.current++}`;
const i = 8+Number(lgcItem.value);
const newPanes = [...items];
newPanes.push({
...lgcItem,
children: (
<>
<Form.Item name={[`${lgcItem.value}`, 'title']} label={t('products:Title')}>
<Form.Item name={[i, 'title']} label={t('products:Title')}>
<Input />
</Form.Item>
<Form.Item name={[`${lgcItem.value}`, 'description']} label={t('products:Description')}>
<Form.Item name={[i, 'description']} label={t('products:Description')}>
<Input.TextArea rows={3} />
</Form.Item>
<Form.Item hidden name={[`${lgcItem.value}`, 'lgc']} initialValue={lgcItem.value}>
<Form.Item hidden name={[i, 'lgc']} initialValue={lgcItem.value}>
<Input />
</Form.Item>
<Form.Item hidden name={[`${lgcItem.value}`, 'id']}>
<Form.Item hidden name={[i, 'id']}>
<Input />
</Form.Item>
</>
@ -94,7 +95,7 @@ const ProductInfoLgc = ({ formInstance, ...props }) => {
});
setItems(newPanes);
setActiveKey(newActiveKey);
const currentLgcOptions = cloneDeep(lgcOptions);
const currentLgcOptions = structuredClone(lgcOptions);
currentLgcOptions.splice(currentLgcOptions.findIndex(ele => ele.value === lgcItem.value), 1);
setLgcOptions(currentLgcOptions);
setSelectNewLgc(null);
@ -143,7 +144,7 @@ const ProductInfoLgc = ({ formInstance, ...props }) => {
return (
<>
<Form.List name={'lgc_details'}>
{(fields, { add, remove }) => <Tabs type='editable-card' size='small' onChange={onChange} activeKey={activeKey} onEdit={onEdit} items={items} hideAdd={isEmpty(lgcOptions)} tabPosition='top' />}
{(fields, { add, remove }) => <Tabs type='editable-card' size='small' onChange={onChange} activeKey={activeKey} onEdit={onEdit} items={items} hideAdd={isEmpty(lgcOptions) || !editable} tabPosition='top' />}
</Form.List>
<Modal title='选择语言' open={newLgcModalVisible} onOk={handleOk} onCancel={() => setNewLgcModalVisible(false)}>
<Select

@ -1,14 +1,15 @@
import { createContext, useEffect, useState } from 'react';
import { Table, Form, Modal, Button, InputNumber, Select, DatePicker } from 'antd';
import { Table, Form, Modal, Button, InputNumber, Select, DatePicker, Popconfirm } from 'antd';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';
import BatchImportPrice from './BatchImportPrice';
import { useDatePresets } from '@/hooks/useDatePresets';
import { useDatePresets, useWeekdays } from '@/hooks/useDatePresets';
const { RangePicker } = DatePicker;
const ProductInfoQuotation = ({ formInstance, ...props }) => {
const ProductInfoQuotation = ({ editable, formInstance, ...props }) => {
const { t } = useTranslation();
const presets = useDatePresets();
const weekdaySets = useWeekdays();
const [batchImportPriceVisible, setBatchImportPriceVisible] = useState(false);
const [quotationTableVisible, setQuotationTableVisible] = useState(false);
const [editIndex, setEditIndex] = useState(null);
@ -91,8 +92,7 @@ const ProductInfoQuotation = ({ formInstance, ...props }) => {
setQuotationTableVisible(false);
};
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
const handleDayClick = (dayIndex) => {
const dayOfWeek = (dayIndex % 7) + 1;
const handleDayClick = (dayOfWeek) => {
setSelectedDays((prevSelectedDays) => {
const updatedDays = prevSelectedDays.includes(dayOfWeek) ? prevSelectedDays.filter((d) => d !== dayOfWeek) : [...prevSelectedDays, dayOfWeek];
@ -101,34 +101,78 @@ const ProductInfoQuotation = ({ formInstance, ...props }) => {
return updatedDays;
});
};
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 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:adultPrice'), dataIndex: 'adult_cost', width: '4rem' },
{ title: t('products:childrenPrice'), dataIndex: 'child_cost', width: '4rem' },
{ title: t('products:currency'), dataIndex: 'currency', width: '4rem' },
{
title: t('products:Types'),
dataIndex: 'unit',
width: '10%',
editable: true,
width: '4rem',
render: (text) => (text === '0' ? '每人' : text === '1' ? '每团' : text),
},
{
title: t('products:number'),
dataIndex: 'group_size',
width: '20%',
editable: true,
width: '4rem',
render: (_, record) => `${record.group_size_min}-${record.group_size_max}`,
},
{
title: t('products:validityPeriod'),
dataIndex: 'validityPeriod',
width: '20%',
editable: true,
width: '6rem',
render: (_, record) => `${record.use_dates_start}-${record.use_dates_end}`,
},
{ title: t('products:Weekdays'), dataIndex: 'weekdays', width: '10%' },
{ title: t('products:Weekdays'), dataIndex: 'weekdays', width: '4rem' },
...(editable
? [
{
title: t('products:operation'),
dataIndex: 'operation',
width: '6rem',
render: (_, record, index) => {
const canEdit = true; // record.audit_state_id === -1; // todo:
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>
);
},
},
]
: []),
];
return (
<>
@ -143,13 +187,14 @@ const ProductInfoQuotation = ({ formInstance, ...props }) => {
// pagination={{ onChange: cancel }}
/>
{
// !isCanEditable &&
editable &&
<Button onClick={handleAdd} type='primary' ghost style={{ marginTop: 16 }}>
{t('products:addQuotation')}
</Button>
}
{
editable &&
<Button onClick={handleBatchImport} type='primary' ghost style={{ marginTop: 16, marginLeft: 16 }}>
批量添加
</Button>
@ -208,9 +253,9 @@ const ProductInfoQuotation = ({ formInstance, ...props }) => {
}}
/>
<h3>周末</h3>
{days.map((day, index) => (
<Button key={index} type={selectedDays.includes((index % 7) + 1) ? 'primary' : 'default'} onClick={() => handleDayClick(index)} style={{ margin: '5px' }}>
{day}
{weekdaySets.map((day, index) => (
<Button key={day.value} type={selectedDays.includes(day.value) ? 'primary' : 'default'} onClick={() => handleDayClick(day.value)} style={{ margin: '5px' }}>
{day.label}
</Button>
))}
</Modal>

@ -49,7 +49,8 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
const [autoExpandParent, setAutoExpandParent] = useState(true);
useEffect(() => {
// ; , ; ,
// ;
// const title = text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '';
const hasDataTypes = Object.keys(agencyProducts);
const _show = productsTypes
.filter((kk) => hasDataTypes.includes(kk.value))
@ -57,12 +58,14 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
...ele,
title: ele.label,
key: ele.value,
children: (agencyProducts[ele.value] || []).map((product) => ({
title: product.info.title,
children: (agencyProducts[ele.value] || []).map((product) => {
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 || '',
// key: `${ele.value}-${product.info.id}`,
key: product.info.id,
_raw: product,
})),
}}),
}));
setTreeData(_show);
setRawTreeData(_show);
@ -121,6 +124,7 @@ const ProductsTree = ({ onNodeSelect, ...props }) => {
<Tree
blockNode
showLine
selectedKeys={[editingProduct?.info?.id || editingProduct?.info?.product_type_id]}
switcherIcon={<CaretDownOutlined />}
onSelect={handleNodeSelect}
treeData={treeData}

Loading…
Cancel
Save