供应商搜索产品页面

feature/price_manager
Lei OT 1 year ago
parent ca0edbd63a
commit 1142a1a893

@ -51,7 +51,8 @@
"Feedback": "Feedback",
"Notice": "Notice",
"Report": "Report",
"Airticket": "AirTicket"
"Airticket": "AirTicket",
"Products": "Products"
},
"Validation": {
"Title": "Notification",

@ -0,0 +1,26 @@
{
"type": {
"Experience": "Experience",
"Car": "Transport Services",
"Guide": "Guide Services",
"Package": "Package",
"Attractions": "Attractions",
"Meals": "Meals",
"Extras": "Extras",
"Special": "Special"
},
"auditState": {
"New": "New",
"Pending": "Pending",
"Approve": "Approve",
"Rejected": "Rejected",
"Published": "Published"
},
"Title": "Title",
"Vendor": "Vendor",
"AuState": "Audit State",
"CreatedBy": "Created By",
"CreateDate": "Create Date",
"Auditors": "Auditors",
"AuditDate": "Audit Date"
}

@ -51,7 +51,8 @@
"Feedback": "反馈表",
"Notice": "通知",
"Report": "质量评分",
"Airticket": "机票订票"
"Airticket": "机票订票",
"Products": "产品管理"
},
"Validation": {
"Title": "温馨提示",

@ -0,0 +1,26 @@
{
"type": {
"Experience": "综费",
"Car": "车费",
"Guide": "导服",
"Package": "包价线路",
"Attractions": "景点",
"Meals": "餐费",
"Extras": "附加",
"Special": "特殊项目"
},
"auditState": {
"New": "新增",
"Pending": "待审核",
"Approve": "已通过",
"Rejected": "已拒绝",
"Published": "已发布"
},
"Title": "名称",
"Vendor": "供应商",
"AuState": "审核状态",
"CreatedBy": "提交人员",
"CreateDate": "提交时间",
"Auditors": "审核人员",
"AuditDate": "审核时间"
}

@ -5,6 +5,16 @@ import { DATE_FORMAT, SMALL_DATETIME_FORMAT } from '@/config';
import useFormStore from '@/stores/Form';
import usePresets from '@/hooks/usePresets';
import { useTranslation } from 'react-i18next';
import SearchInput from './SearchInput';
import { fetchJSON } from '@/utils/request';
import { HT_HOST } from '@/config';
//
export const fetchVendorList = async () => {
const { errcode, Result } = await fetchJSON(`${HT_HOST}/service-cusservice/PTGetHWVendorList`)
return errcode !== 0 ? [] : Result
}
const { RangePicker } = DatePicker;
@ -28,6 +38,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => {
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) : '') },
@ -36,6 +47,13 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => {
{ key: 'endtime', transform: (arrVal) => (arrVal ? arrVal[1].format(SMALL_DATETIME_FORMAT) : '') },
],
'invoiceStatus': { key: 'invoiceStatus', transform: (value) => value?.value || value?.key || '', default: '' },
'auditStatus': { key: 'auditStatus', 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 : '';
},
},
};
let dest = {};
const { dates, ...omittedValue } = values;
@ -57,9 +75,9 @@ const SearchForm = ({ initialValue, onSubmit, onReset, ...props }) => {
}, []);
const onFinish = (values) => {
console.log('Received values of form, origin form value: ', values);
console.log('Received values of form, origin form value: \n', values);
const dest = formValuesMapper(values);
console.log('form value send to onSubmit:', dest);
console.log('form value send to onSubmit:\n', dest);
const str = new URLSearchParams(dest).toString();
setFormValues(values);
setFormValuesToSub(dest);
@ -132,17 +150,25 @@ function getFields(props) {
};
let baseChildren = [];
baseChildren = [
item(
'keyword',
99,
<Form.Item name='keyword' {...fieldProps.keyword}>
<Input allowClear {...fieldComProps.keyword} />
</Form.Item>,
fieldProps?.keyword?.col || 6
),
item(
'referenceNo',
1,
99,
<Form.Item name='referenceNo' label={t('group:RefNo')} {...fieldProps.referenceNo}>
<Input placeholder={t('group:RefNo')} allowClear />
</Form.Item>,
fieldProps?.referenceNo?.col || 4
fieldProps?.referenceNo?.col || 6
),
item(
'PNR',
2,
99,
<Form.Item name='PNR' label="PNR">
<Input placeholder={t('group:PNR')} allowClear />
</Form.Item>,
@ -176,6 +202,36 @@ function getFields(props) {
</Form.Item>,
fieldProps?.dates?.col || midCol
),
/**
*
*/
item(
'agency',
99,
<Form.Item name='agency' label={t('products:Vendor')} {...fieldProps.agency} initialValue={at(props, 'initialValue.agency')[0]}>
<SearchInput placeholder={t('products:Vendor')} mode={'multiple'} maxTagCount={0} {...fieldComProps.agency} fetchOptions={fetchVendorList} map={{VEI2_CompanyBN: 'label', VEI_SN: 'value'}} />
</Form.Item>,
fieldProps?.agency?.col || 6
),
item(
'auditState',
99,
<Form.Item name={`auditState`} initialValue={at(props, 'initialValue.auditState')[0] || { value: '0', label: 'Status' }}>
<Select
style={{ width: '100%' }}
labelInValue
allowClear
options={[ // todo:
{ value: '0', label: 'New' },
{ value: '1', label: 'Pending' },
{ value: '2', label: 'Approve' },
{ value: '3', label: 'Rejected' },
{ value: '4', label: 'Published' },
]}
/>
</Form.Item>,
fieldProps?.auditState?.col || 3
),
];
baseChildren = baseChildren

@ -0,0 +1,48 @@
import React, { useMemo, useRef, useState } from 'react';
import { Select, Spin } from 'antd';
import { debounce, objectMapper } from '@/utils/commons';
function DebounceSelect({ fetchOptions, debounceTimeout = 800, ...props }) {
const [fetching, setFetching] = useState(false);
const [options, setOptions] = useState([]);
const fetchRef = useRef(0);
const debounceFetcher = useMemo(() => {
const loadOptions = (value) => {
fetchRef.current += 1;
const fetchId = fetchRef.current;
setOptions([]);
setFetching(true);
fetchOptions(value).then((newOptions) => {
const mapperOptions = newOptions.map(ele => objectMapper(ele, props.map));
if (fetchId !== fetchRef.current) {
// for fetch callback order
return;
}
setOptions(mapperOptions);
setFetching(false);
});
};
return debounce(loadOptions, debounceTimeout);
}, [fetchOptions, debounceTimeout]);
return (
<Select
labelInValue
filterOption={false}
showSearch
allowClear
maxTagCount={1}
{...props}
onSearch={debounceFetcher}
notFoundContent={fetching ? <Spin size='small' /> : null}
optionFilterProp='label'
>
{options.map((d) => (
<Select.Option key={d.value} title={d.label}>
{d.label}
</Select.Option>
))}
</Select>
);
}
export default DebounceSelect;

@ -0,0 +1,46 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
/**
* 产品管理 相关的预设数据
*/
export const useProductsTypes = () => {
const [types, setTypes] = useState([]);
const { t, i18n } = useTranslation();
useEffect(() => {
const newData = [
{ label: t('products:type.Experience'), value: t('products:type.Experience') },
{label: t("products:type.Car"), value: t("products:type.Car"),},
{label: t("products:type.Guide"), value: t("products:type.Guide"),},
{label: t("products:type.Package"), value: t("products:type.Package"),},
{label: t("products:type.Attractions"), value: t("products:type.Attractions"),},
{label: t("products:type.Meals"), value: t("products:type.Meals"),},
{label: t("products:type.Extras"), value: t("products:type.Extras"),},
{label: t("products:type.Special"), value: t("products:type.Special")},
];
setTypes(newData);
}, [i18n.language]);
return types;
};
export const useProductsAuditStatus = () => {
const [types, setTypes] = useState([]);
const { t, i18n } = useTranslation();
useEffect(() => {
const newData = [
{ value: '0', label: 'New' },
{ value: '1', label: 'Pending' },
{ value: '2', label: 'Approve' },
{ value: '3', label: 'Rejected' },
{ value: '4', label: 'Published' },
];
setTypes(newData);
}, [i18n.language]);
return types;
};

@ -17,7 +17,7 @@ i18n
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
},
ns: ['common', 'group', 'vendor', 'account'],
ns: ['common', 'group', 'vendor', 'account', 'products'],
defaultNS: 'common',
detection: {
// convertDetectedLanguage: 'Iso15897',

@ -30,6 +30,7 @@ import InvoiceDetail from "@/views/invoice/Detail";
import InvoicePaid from "@/views/invoice/Paid";
import InvoicePaidDetail from "@/views/invoice/PaidDetail";
import Airticket from "@/views/airticket/Index";
import ProductsIndex from '@/views/products/Index';
import './i18n';
@ -65,6 +66,7 @@ const router = createBrowserRouter([
{ path: "invoice/paid",element:<InvoicePaid />},
{ path: "invoice/paid/detail/:flid",element:<InvoicePaidDetail />},
{ path: "airticket",element:<Airticket />},
{ path: "products",element:<ProductsIndex />},
]
},
{

@ -0,0 +1,24 @@
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
import { fetchJSON } from '@/utils/request';
import { HT_HOST } from '@/config';
const initialState = {
loading: false,
productsList: [],
};
export const useProductsStore = create(
devtools((set, get) => ({
// 初始化状态
...initialState,
// state actions
setProductsList: (productsList) => set({ productsList }),
reset: () => set(initialState),
// side effects
}))
);
export default useProductsStore;

@ -125,6 +125,7 @@ function App() {
{ key: 'feedback', label: <Link to='/feedback'>{t('menu.Feedback')}</Link> },
{ key: 'report', label: <Link to='/report'>{t('menu.Report')}</Link> },
{ key: 'airticket', label: <Link to='/airticket'>{t('menu.Airticket')}</Link> },
{ key: 'products', label: <Link to='/products'>{t('menu.Products')}</Link> },
{
key: 'notice',
label: (
@ -151,6 +152,7 @@ function App() {
{ label: <Link to='/account/management'>{t('account:management.tile')}</Link>, key: '3' },
{ type: 'divider' },
{ label: <Link to='/logout'>{t('Logout')}</Link>, key: '4' },
{ label: <Link to='/account/change-vendor'>{t('ChangeVendor')}</Link>, key: 'change-vendor' },
],
{ type: 'divider' },
{ label: <>v{BUILD_VERSION}</>, key: 'BUILD_VERSION' },

@ -82,7 +82,7 @@ function Index() {
fieldsConfig={{
shows: ['referenceNo', 'invoiceStatus', 'dates'],
fieldProps: {
referenceNo: { col: 5 },
referenceNo: { col: 7 },
invoiceStatus: { col: 4},
dates: { col: 10 },
},

@ -0,0 +1,55 @@
import { useEffect, useState } from 'react';
import { Row, Col, Space, Typography, Table, Button } from 'antd';
import useProductsStore from '@/stores/Products/Index';
import { usingStorage } from '@/hooks/usingStorage';
import SearchForm from '@/components/SearchForm';
import dayjs from 'dayjs';
import { useTranslation } from 'react-i18next';
import { useProductsTypes } from '@/hooks/useProductsSets';
function Index() {
const { t } = useTranslation();
const { userId } = usingStorage();
const [loading, productsList] = useProductsStore((state) => state.productsList);
const [noticeList, setNoticeList] = useState([]);
useEffect(() => {}, []);
const showTotal = (total) => `Total ${total} items`;
const columns = [
{ title: t('products:Vendor'), key: '', dataIndex: '' },
{ title: t('products:AuState'), key: '', dataIndex: '' },
{ title: t('products:CreatedBy'), key: '', dataIndex: '' },
{ title: t('products:CreateDate'), key: '', dataIndex: '' },
{ title: t('products:Auditors'), key: '', dataIndex: '' },
{ title: t('products:AuditDate'), key: '', dataIndex: '' },
{ title: '', key: 'action', render: () => <Button>{t('Edit')}</Button> },
];
return (
<Space direction='vertical' style={{ width: '100%' }}>
<SearchForm
initialValue={{
dates: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
}}
fieldsConfig={{
shows: ['agency', 'auditState', 'dates', 'keyword'], // todo:
fieldProps: {
dates: { label: t('products:CreateDate') },
keyword: { label: t('products:Title')},
},
sort: { agency: 1, auditState: 2, keyword: 100},
}}
onSubmit={(err, formVal, filedsVal) => {
}}
/>
<Row>
<Col md={24} lg={24} xxl={24}>
<Table bordered={true} columns={columns} dataSource={productsList} pagination={{ defaultPageSize: 20, showTotal: showTotal }} loading={loading} />
</Col>
<Col md={24} lg={24} xxl={24}></Col>
</Row>
</Space>
);
}
export default Index;
Loading…
Cancel
Save