feat: 产品: 附加项目: 新增; 删除; 读取

feature/price_manager
Lei OT 1 year ago
parent 1b97cb41dd
commit 2bdf22c140

@ -10,6 +10,7 @@
"Confirm": "Confirm", "Confirm": "Confirm",
"Close": "Close", "Close": "Close",
"Save": "Save", "Save": "Save",
"New": "New",
"Edit": "Edit", "Edit": "Edit",
"Audit": "Audit", "Audit": "Audit",
"Delete": "Delete", "Delete": "Delete",
@ -28,6 +29,9 @@
"sureCancel": "Sure you want to cancel?", "sureCancel": "Sure you want to cancel?",
"sureDelete":"Sure you want to delete?", "sureDelete":"Sure you want to delete?",
"Success": "Success",
"Failed": "Failed",
"Table": { "Table": {
"Total": "Total {{total}} items" "Total": "Total {{total}} items"
}, },

@ -10,6 +10,11 @@
"Overtravel": "超公里", "Overtravel": "超公里",
"Special": "Special" "Special": "Special"
}, },
"Components": {
"info": "Product Information",
"Quotation": "Quotation",
"Extras": "Add-on"
},
"auditState": { "auditState": {
"New": "New", "New": "New",
"Pending": "Pending", "Pending": "Pending",

@ -10,6 +10,7 @@
"Confirm": "确认", "Confirm": "确认",
"Close": "关闭", "Close": "关闭",
"Save": "保存", "Save": "保存",
"New": "新增",
"Edit": "编辑", "Edit": "编辑",
"Audit": "审核", "Audit": "审核",
"Delete": "删除", "Delete": "删除",
@ -28,6 +29,9 @@
"sureCancel": "确定取消?", "sureCancel": "确定取消?",
"sureDelete":"确定删除?", "sureDelete":"确定删除?",
"Success": "成功",
"Failed": "失败",
"Table": { "Table": {
"Total": "共 {{total}} 条" "Total": "共 {{total}} 条"
}, },

@ -10,6 +10,11 @@
"Overtravel": "超公里", "Overtravel": "超公里",
"Special": "特殊项目" "Special": "特殊项目"
}, },
"Components": {
"info": "产品信息",
"Quotation": "报价",
"Extras": "附加项目"
},
"auditState": { "auditState": {
"New": "新增", "New": "新增",
"Pending": "待审核", "Pending": "待审核",

@ -1,7 +1,7 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { devtools } from 'zustand/middleware'; import { devtools } from 'zustand/middleware';
import { fetchJSON, postForm } from '@/utils/request'; import { fetchJSON, postForm, postJSON } from '@/utils/request';
import { HT_HOST } from '@/config'; import { HT_HOST } from '@/config';
import { groupBy } from '@/utils/commons'; import { groupBy } from '@/utils/commons';
@ -15,6 +15,22 @@ export const getAgencyProductsAction = async (param) => {
return errcode !== 0 ? { agency: {}, products: [] } : result; return errcode !== 0 ? { agency: {}, products: [] } : result;
}; };
/**
* todo:
*/
export const addProductExtraAction = async (body) => {
const { errcode, result } = await postJSON(`${HT_HOST}/products/extras`, body);
return errcode === 0 ? true : false;
};
/**
* todo:
*/
export const delProductExtrasAction = async (body) => {
const { errcode, result } = await postJSON(`${HT_HOST}/products/extras/del`, body);
return errcode === 0 ? true : false;
};
/** /**
* 获取指定产品的附加项目 * 获取指定产品的附加项目
* @param {object} param { id, travel_agency_id, use_year } * @param {object} param { id, travel_agency_id, use_year }

@ -113,8 +113,14 @@ export function postForm(url, data) {
} }
export function postJSON(url, obj) { export function postJSON(url, obj) {
const initParams = getRequestInitParams();
const params4get = Object.assign({}, initParams);
const params = new URLSearchParams(params4get).toString();
const ifp = url.includes('?') ? '&' : '?';
const fUrl = params !== '' ? `${url}${ifp}${params}` : url;
const headerObj = getRequestHeader() const headerObj = getRequestHeader()
return fetch(url, { return fetch(fUrl, {
method: 'POST', method: 'POST',
body: JSON.stringify(obj), body: JSON.stringify(obj),
headers: { headers: {

@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
import DateComponent from '@/components/date'; import DateComponent from '@/components/date';
import { fetchJSON } from "@/utils/request"; import { fetchJSON } from "@/utils/request";
import Extras from './Detail/Extras';
function Index() { function Index() {
@ -561,7 +562,7 @@ function Index() {
</Card> </Card>
<Card style={{ width: "80%" }}> <Card style={{ width: "80%" }}>
<h2>{t('products:bindingProducts')}</h2> {/* <h2>{t('products:bindingProducts')}</h2>
<Form.Item name="extras"> <Form.Item name="extras">
<Table <Table
components={componentsBinding} components={componentsBinding}
@ -574,7 +575,8 @@ function Index() {
<Button onClick={handleAddBinding} type="primary" style={{ marginBottom: 16 }}> <Button onClick={handleAddBinding} type="primary" style={{ marginBottom: 16 }}>
{t('products:addBinding')} {t('products:addBinding')}
</Button> </Button>
</Form.Item> </Form.Item> */}
<Extras productId={2} />
</Card> </Card>

@ -0,0 +1,173 @@
import { createContext, useContext, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { App, Table, Button, Modal, Popconfirm } from 'antd';
import useProductsStore, { postProductsQuoteAudit, getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index';
import { isEmpty, cloneDeep } from '@/utils/commons';
import SearchForm from '@/components/SearchForm';
import RequireAuth from '@/components/RequireAuth';
import { PERM_PRODUCTS_MANAGEMENT } from '@/config';
const NewAddonModal = ({ onPick, ...props }) => {
const { travel_agency_id, use_year } = useParams();
const { t } = useTranslation();
const { notification, message } = App.useApp();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false); // bind loading
const [searchLoading, setSearchLoading] = useState(false);
const [searchResult, setSearchResult] = useState([]);
const onSearchProducts = async (values) => {
const copyObject = cloneDeep(values);
const { starttime, endtime, ...param } = copyObject;
setSearchLoading(true);
setSearchResult([]);
const result = await getAgencyProductsAction({ ...param, audit_state: '0', travel_agency_id, use_year });
setSearchResult(result?.products || []);
setSearchLoading(false);
};
const handleAddExtras = async (item) => {
// const success = await fetchBindOrder({ coli_sn, conversationid: currentConversationID });
// success ? message.success('') : message.error('');
// setOpen(false);
if (typeof onPick === 'function') {
onPick(item);
}
};
// todo:
const searchResultColumns = [
{ key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('products:Title') },
{
title: t('products:price'),
dataIndex: ['quotation', '0', 'value'],
width: '10rem',
render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo:
},
{
key: 'action',
title: '',
width: 150,
render: (_, record) => (
<Button className='text-primary' onClick={() => handleAddExtras(record)}>
附加此项目
</Button>
),
},
];
const paginationProps = {
showTotal: (total) => t('Table.Total', { total }),
};
return (
<>
<Button type='primary' onClick={() => setOpen(true)} className='mt-2'>
{t('New')}
</Button>
<Modal width={'95%'} style={{ top: 20 }} open={open} title={'添加附加'} footer={false} onCancel={() => setOpen(false)} destroyOnClose>
<SearchForm
fieldsConfig={{
shows: ['dates', 'year', 'keyword'],
fieldProps: {
dates: { label: t('products:CreateDate') },
keyword: { label: t('products:Title'), col: 4 },
},
// sort: { keyword: 100 },
}}
initialValue={
{
// dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
// year: dayjs().add(1, 'year'),
}
}
onSubmit={(err, formVal, filedsVal) => {
onSearchProducts(formVal);
}}
/>
<Table
size={'small'}
key={'searchProductsTable'}
loading={searchLoading}
dataSource={searchResult}
columns={searchResultColumns}
pagination={searchResult.length <= 10 ? false : paginationProps}
/>
</Modal>
</>
);
};
/**
*
*/
const Extras = ({ productId, onChange, ...props }) => {
const { t } = useTranslation();
const { notification, message } = App.useApp();
const { travel_agency_id, use_year } = useParams();
const [extrasData, setExtrasData] = useState([]);
const handleGetAgencyProductExtras = async () => {
const data = await getAgencyProductExtrasAction({ id: productId, travel_agency_id, use_year });
setExtrasData(data);
};
const handleNewAddOn = async (item) => {
setExtrasData(prev => [].concat(prev, [item]));
// todo: ;
const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [2] });
newSuccess ? message.success(t('Success')) : message.error(t('Failed'));
await handleGetAgencyProductExtras();
}
const handleDelAddon = async (item) => {
// todo:
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [2] });
delSuccess ? message.success(t('Success')) : message.error(t('Failed'));
await handleGetAgencyProductExtras();
};
useEffect(() => {
handleGetAgencyProductExtras();
return () => {};
}, []);
const columns = [
{ title: t('products:Title'), dataIndex: ['info', 'title'], width: '16rem', },
{
title: t('products:Offer'),
dataIndex: ['quotation', '0', 'value'],
width: '10rem',
render: (_, { quotation }) => `${quotation[0].value} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo:
},
// { title: t('products:Types'), dataIndex: 'age_type', width: '40%', },
{
title: '',
dataIndex: 'operation',
width: '4rem',
render: (_, r) => (
<Popconfirm title={t('products:sureDelete')} onConfirm={(e) => handleDelAddon(r)} >
<Button size='small' type='link' danger>
{t('Delete')}
</Button>
</Popconfirm>
),
},
];
return (
<>
<RequireAuth subject={PERM_PRODUCTS_MANAGEMENT}>
<h2>{t('products:Components.Extras')}</h2>
<Table dataSource={extrasData} columns={columns} bordered pagination={false} rowKey={(r) => r.info.id} />
<NewAddonModal onPick={handleNewAddOn} />
</RequireAuth>
</>
);
};
export default Extras;
Loading…
Cancel
Save