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

perf/export-docx
黄文强@HWQ-PC 1 year ago
commit 5d78c9ee72

@ -8,5 +8,29 @@
"CurrentPassword": "Please input your password.",
"NewPassword": "Please input your new password.",
"ReenterPassword": "Please reenter your password."
}
},
"createdOn": "Created on",
"action": "Action",
"action.edit": "Edit",
"action.enable": "Enable",
"action.disable": "Disable",
"action.enable.title": "Do you want to enable account?",
"action.disable.title": "Do you want to disable account?",
"action.resetPassword": "Reset Password",
"action.resetPassword.tile": "Do you want to reset password?",
"accountList": "Account List",
"newAccount": "New Account",
"detail": "Detail",
"username": "Username",
"realname": "Realname",
"travelAgency": "Travel Agency",
"travelAgencyName": "Travel Agency Name",
"email": "Email",
"lastLogin": "Last Login",
"roleList": "Role List",
"newRole": "New Role",
"roleName": "Role Name",
"permission": "Permission"
}

@ -17,6 +17,7 @@
"action.enable.title": "确定启用该账号吗?",
"action.disable.title": "确定禁用该账号吗?",
"action.resetPassword": "重置密码",
"action.resetPassword.tile": "确定重置账号密码吗?",
"accountList": "管理账号",
"newAccount": "新增账号",

@ -52,6 +52,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, fo
const formValuesMapper = (values) => {
const destinationObject = {
'keyword': { key: 'keyword', transform: (value) => value || '' },
'username': { key: 'username', transform: (value) => value || '' },
'referenceNo': { key: 'referenceNo', transform: (value) => value || '' },
'dates': [
{ key: 'startdate', transform: (arrVal) => (arrVal ? arrVal[0].format(DATE_FORMAT) : '') },
@ -83,6 +84,7 @@ const SearchForm = ({ initialValue, onSubmit, onReset, confirmText, formName, fo
return Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : '';
},
},
'unconfirmed': { key: 'unconfirmed', transform: (value) => value ? 1 : 0 },
};
let dest = {};
const { dates, ...omittedValue } = values;
@ -243,14 +245,6 @@ function getFields(props) {
</Form.Item>,
fieldProps?.username?.col || 4
),
item(
'realname',
99,
<Form.Item name='realname' label={t('account:realname')} {...fieldProps.realname}>
<Input placeholder={t('account:realname')} allowClear />
</Form.Item>,
fieldProps?.realname?.col || 4
),
/**
*
*/

@ -145,7 +145,6 @@ const useAccountStore = create((set, get) => ({
}
const searchParams = {
username: formValues.username,
realname: formValues.realname,
travel_agency_ids: travel_agency_ids,
lgc: 2
}

@ -4,8 +4,6 @@ import { HT_HOST } from "@/config"
import { loadPageSpy } from '@/pageSpy'
import { usingStorage } from '@/hooks/usingStorage'
import { lifecycleware } from '@/utils/lifecycle'
const KEY_LOGIN_TOKEN = 'G-STR:LOGIN_TOKEN'
const KEY_TRAVEL_AGENCY_ID = 'G-INT:TRAVEL_AGENCY_ID'
const KEY_USER_ID = 'G-INT:USER_ID'
@ -161,20 +159,20 @@ const useAuthStore = create((set, get) => ({
// TODO: 迁移到 Account.js
changeUserPassword: (password, newPassword) => {
const { userId } = usingStorage()
const formData = new FormData();
formData.append('UserID', userId);
formData.append('Password', password);
formData.append('NewPassword', newPassword);
const postUrl = HT_HOST + '/service-CooperateSOA/SetPassword';
const formData = new FormData()
formData.append('UserID', userId)
formData.append('Password', password)
formData.append('NewPassword', newPassword)
const postUrl = HT_HOST + '/service-CooperateSOA/SetPassword'
return postForm(postUrl, formData)
.then(json => {
if (json.errcode == 0) {
return json;
return json
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
throw new Error(json.errmsg + ': ' + json.errcode)
}
});
})
},
isPermitted: (perm) => {

@ -10,6 +10,16 @@ export const searchAgencyAction = async (param) => {
return errcode !== 0 ? [] : result;
};
/**
* 搜索所有产品, 返回产品列表
* ! 只有审核通过, 已发布的
* @param {object} params { keyword, use_year, product_types, travel_agency_id, city }
*/
export const searchPublishedProductsAction = async (param) => {
const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/web_products_search`, param);
return errcode !== 0 ? [] : result;
};
export const copyAgencyDataAction = async (postbody) => {
const formData = new FormData();
Object.keys(postbody).forEach((key) => {

@ -8,7 +8,7 @@ export const fetchCityList = async (travelAgencyId, reservationId) => {
const { errcode, Result } = await fetchJSON(
`${HT_HOST}/service-cusservice/PTGetCityGuide`,
{ VEI_SN: travelAgencyId, GRI_SN: reservationId, LGC: 1 })
return errcode !== 0 ? {} : Result;
return errcode !== 0 ? {} : Result
}
export const fetchPlanDetail = async (travelAgencyId, reservationId) => {
@ -31,7 +31,7 @@ export const fetchAttachList = async (reservationId) => {
const { errcode, result } = await fetchJSON(
`${HT_HOST}/service-fileServer/PlanChangeFileList`,
{ GRI_SN: reservationId })
return errcode !== 0 ? {} : result;
return errcode !== 0 ? {} : result
}
const useReservationStore = create((set, get) => ({
@ -96,22 +96,21 @@ const useReservationStore = create((set, get) => ({
const { reservationPage } = get()
// 设置为 0后端会重新计算总数当跳转第 X 页时可用原来的总数。
const totalNum = current == 1 ? 0 : reservationPage.total
const notConfirmValue = formValues.notConfirm ? 1 : 0
const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/GetPlanSearchList')
.append('VEI_SN', travelAgencyId)
.append('GroupNo', formValues.referenceNo)
.append('DateStart', formValues.startdate)
.append('DateEnd', formValues.enddate)
.append('NotConfirm', notConfirmValue)
.append('NotConfirm', formValues.unconfirmed)
.append('TotalNum', totalNum)
.append('PageSize', reservationPage.size)
.append('PageIndex', current)
.build();
.build()
return fetchJSON(fetchUrl)
.then(json => {
if (json.errcode == 0) {
const mapReservationList = (json?.Result??[]).map((data, index) => {
const mapReservationList = (json?.Result??[]).map((data) => {
return {
key: data.vas_gri_sn,
reservationId: data.vas_gri_sn,
@ -133,32 +132,32 @@ const useReservationStore = create((set, get) => ({
}
}))
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
throw new Error(json.errmsg + ': ' + json.errcode)
}
});
})
},
fetchAllGuideList: () => {
const { travelAgencyId } = usingStorage()
const fetchUrl = prepareUrl(HT_HOST + '/service-cusservice/PTGetGuideList')
.append('VEI_SN', travelAgencyId)
.build();
.build()
return fetchJSON(fetchUrl)
.then(json => {
if (json.errcode == 0) {
const guideList = (json?.Result??[]).map((data, index) => {
const guideList = (json?.Result??[]).map((data) => {
return {
guideId: data.TGI_SN,
guideName: data.TGI2_Name,
mobileNo: data.TGI_Mobile
}
});
return guideList;
})
return guideList
} else {
throw new Error(json.errmsg + ': ' + json.errcode);
throw new Error(json.errmsg + ': ' + json.errcode)
}
});
})
},
getReservationDetail: async (reservationId) => {
@ -168,8 +167,8 @@ const useReservationStore = create((set, get) => ({
const mapConfirmationList = planChangeList.map((data) => {
const filterAttchList = attachListJson.filter(attch => {
return attch.PCI_SN === data.PCI_SN;
});
return attch.PCI_SN === data.PCI_SN
})
return {
key: data.PCI_SN,
PCI_Changetext: data.PCI_Changetext,
@ -210,7 +209,7 @@ const useReservationStore = create((set, get) => ({
getReservationDetail(travelAgencyId, reservationDetail.reservationId)
return json
}
});
})
},
setupCityGuide: (cityId, guideId) => {
@ -230,7 +229,7 @@ const useReservationStore = create((set, get) => ({
if (json.errcode != 0) {
throw new Error(json.errmsg + ': ' + json.errcode)
}
});
})
},
@ -242,7 +241,7 @@ const useReservationStore = create((set, get) => ({
.append('VEI_SN', travelAgencyId)
.append('GRI_SN', selectedReservation.reservationId)
.append('LGC', 1)
.build();
.build()
return fetchJSON(fetchUrl)
.then(json => {
@ -253,10 +252,6 @@ const useReservationStore = create((set, get) => ({
return data.GuideName
}).join(',')
runInAction(() => {
selectedReservation.guide = reservationGuide
})
set((state) => ({
selectedReservation: {
...state.selectedReservation,
@ -272,4 +267,4 @@ const useReservationStore = create((set, get) => ({
}
}))
export default useReservationStore
export default useReservationStore

@ -103,7 +103,7 @@ function App() {
<Layout className='min-h-screen'>
<Header className='sticky top-0 z-10 w-full'>
<Row gutter={{ md: 24 }} justify='end' align='middle'>
<Col span={16}>
<Col span={14}>
<NavLink to='/'>
<img src={AppLogo} className='float-left h-9 my-4 mr-6 ml-0 bg-white/30' alt='App logo' />
</NavLink>
@ -130,7 +130,7 @@ function App() {
]}
/>
</Col>
<Col span={4}>
<Col span={6}>
<h3 className='text-white mb-0 flex justify-end'>
{currentUser?.travelAgencyName}
</h3>

@ -193,11 +193,12 @@ function Management() {
}
const showResetPasswordConfirm = (account) => {
const confirmTitle = t('account:action.resetPassword.tile')
const randomPassword = account.username + '@' + (Math.floor(Math.random() * 900) + 100)
modal.confirm({
title: 'Do you want to reset password?',
title: confirmTitle,
icon: <ExclamationCircleFilled />,
content: `Username: ${account.username}, Realname: ${account.realname}`,
content: t('account:username') + ': ' + account.username + ', ' + t('account:realname') + ': ' + account.realname,
onOk() {
resetAccountPassword(account.userId, randomPassword)
.then(() => {
@ -324,13 +325,12 @@ function Management() {
<Title level={3}>{t('account:accountList')}</Title>
<SearchForm
fieldsConfig={{
shows: ['username', 'realname', 'agency'],
shows: ['username', 'agency'],
fieldProps: {
username: { label: t('account:username') },
realname: { label: t('account:realname') },
username: { label: t('account:username') + '/' + t('account:realname') },
agency: { label: t('account:travelAgency') },
},
sort: { username: 1, realname: 2, agency: 3},
sort: { username: 1, agency: 2},
}}
onSubmit={() => {
handelAccountSearch()

@ -6,7 +6,10 @@ import SecondHeaderWrapper from '@/components/SecondHeaderWrapper';
import { useTranslation } from 'react-i18next';
import useProductsStore, { postProductsQuoteAuditAction, } from '@/stores/Products/Index';
import { isEmpty } from '@/utils/commons';
import useAuthStore from '@/stores/Auth';
import RequireAuth from '@/components/RequireAuth';
// import PrintContractPDF from './PrintContractPDF';
import { PERM_PRODUCTS_OFFER_AUDIT, PERM_PRODUCTS_OFFER_PUT } from '@/config';
const Header = ({ title, agency, refresh, ...props }) => {
const { travel_agency_id, use_year, audit_state } = useParams();
@ -35,20 +38,32 @@ const Header = ({ title, agency, refresh, ...props }) => {
return (
<div className='flex justify-end items-center gap-4 h-full'>
<div className='grow'>
<h2 className='m-0 leading-tight'>{title}<Divider type={'vertical'} />{(use_year || '').replace('all', '')}</h2>
<h2 className='m-0 leading-tight'>
{title}
<Divider type={'vertical'} />
{(use_year || '').replace('all', '')}
</h2>
</div>
{/* <Button size='small'>{t('Copy')}</Button> */}
{/* <Button size='small'>{t('Import')}</Button> */}
<Link className='px-2' to={`/products/${travel_agency_id}/${use_year}/${audit_state}/edit`}>{t('Edit')}</Link>
<Button size='small' type={'primary'} onClick={() => handleAuditItem('2', agency)}>
{t('products:auditStateAction.Published')}
</Button>
<RequireAuth subject={PERM_PRODUCTS_OFFER_PUT}>
<Link className='px-2' to={`/products/${travel_agency_id}/${use_year}/${audit_state}/edit`}>
{t('Edit')}
</Link>
</RequireAuth>
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Button size='small' type={'primary'} onClick={() => handleAuditItem('2', agency)}>
{t('products:auditStateAction.Published')}
</Button>
</RequireAuth>
{/* <Button size='small' type={'primary'} ghost onClick={() => handleAuditItem('2', agency)}>
{t('products:auditStateAction.Approved')}
</Button> */}
<Button size='small' type={'primary'} danger ghost onClick={() => handleAuditItem('3', agency)}>
{t('products:auditStateAction.Rejected')}
</Button>
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Button size='small' type={'primary'} danger ghost onClick={() => handleAuditItem('3', agency)}>
{t('products:auditStateAction.Rejected')}
</Button>
</RequireAuth>
{/* todo: export, 审核完成之后才能导出 */}
<Button size='small'>{t('Print')} PDF</Button>
{/* <PrintContractPDF /> */}
@ -59,6 +74,7 @@ const Header = ({ title, agency, refresh, ...props }) => {
const PriceTable = ({ productType, dataSource, refresh }) => {
const { t } = useTranslation('products');
const { travel_agency_id, use_year, audit_state } = useParams();
const isPermitted = useAuthStore(state => state.isPermitted);
const [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]);
const [setEditingProduct] = useProductsStore((state) => [state.setEditingProduct]);
const { message, notification } = App.useApp();
@ -96,7 +112,7 @@ const PriceTable = ({ productType, dataSource, refresh }) => {
const columns = [
{ key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan, }), className: 'bg-white', render: (text, r) => {
const title = text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '';
return <Link to={`/products/${travel_agency_id}/${use_year}/${audit_state}/edit`} onClick={() => setEditingProduct(r.info)}>{title}</Link>;
return isPermitted(PERM_PRODUCTS_OFFER_PUT) ? <Link to={`/products/${travel_agency_id}/${use_year}/${audit_state}/edit`} onClick={() => setEditingProduct(r.info)}>{title}</Link> : title;
} },
// ...(productType === 'B' ? [{ key: 'km', dataIndex: ['info', 'km'], title: t('KM')}] : []),
{ key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_id, unit_name }) => `${adult_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` },
@ -119,7 +135,7 @@ const PriceTable = ({ productType, dataSource, refresh }) => {
key: 'state',
title: t('State'),
render: (_, r) => {
const stateCls = `text-${stateMapVal[`${r.audit_state_id}`]?.color} `;
const stateCls = ` text-${stateMapVal[`${r.audit_state_id}`]?.color} `;
return <span className={stateCls}>{stateMapVal[`${r.audit_state_id}`]?.label}</span>;
},
},
@ -128,10 +144,12 @@ const PriceTable = ({ productType, dataSource, refresh }) => {
key: 'action',
render: (_, r) =>
r.audit_state_id <= 0 ? (
<Space>
<Button onClick={() => handleAuditPriceItem('2', r)}></Button>
<Button onClick={() => handleAuditPriceItem('3', r)}></Button>
</Space>
<RequireAuth subject={PERM_PRODUCTS_OFFER_AUDIT}>
<Space>
<Button onClick={() => handleAuditPriceItem('2', r)}></Button>
<Button onClick={() => handleAuditPriceItem('3', r)}></Button>
</Space>
</RequireAuth>
) : null,
},
];

@ -2,7 +2,7 @@ import { useEffect, useState, useSyncExternalStore } from 'react';
import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { App, Table, Button, Modal, Popconfirm } from 'antd';
import { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index';
import { getAgencyProductExtrasAction, searchPublishedProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index';
import { cloneDeep, pick } from '@/utils/commons';
import SearchForm from '@/components/SearchForm';
@ -27,10 +27,9 @@ const NewAddonModal = ({ onPick, ...props }) => {
const { starttime, endtime, year, ...param } = copyObject;
setSearchLoading(true);
setSearchResult([]);
// debug: audit_state: '1',
const search_year = year || use_year;
const result = await getAgencyProductsAction({ ...param, travel_agency_id, use_year: search_year, audit_state: '0', });
setSearchResult(result?.products || []);
const result = await searchPublishedProductsAction({ ...param, use_year: search_year, });
setSearchResult(result);
setSearchLoading(false);
};
const handleAddExtras = async (item) => {
@ -39,10 +38,10 @@ const NewAddonModal = ({ onPick, ...props }) => {
}
};
// todo:
const searchResultColumns = [
{ key: 'ptype', dataIndex: ['info', 'product_type_id'], width: '6rem', title: t('products:ProductType'), render: (text, r) => productsTypesMapVal[text].label },
{ key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('products:Title') },
{ key: 'ptype', dataIndex: 'type', width: '6rem', title: t('products:ProductType'), render: (text, r) => productsTypesMapVal[text]?.label || text },
{ key: 'code', dataIndex: 'code', width: '6rem', title: t('products:Code') },
{ key: 'title', dataIndex: 'title', width: '16rem', title: t('products:Title') },
// {
// title: t('products:price'),
// dataIndex: ['quotation', '0', 'adult_cost'],
@ -72,7 +71,7 @@ const NewAddonModal = ({ onPick, ...props }) => {
<Modal width={'95%'} style={{ top: 20 }} open={open} title={'添加附加'} footer={false} onCancel={() => setOpen(false)} destroyOnClose>
<SearchForm
fieldsConfig={{
shows: [ 'year', 'keyword'], // 'dates',
shows: [ 'year', 'keyword', 'products_types'], // 'dates',
fieldProps: {
dates: { label: t('products:CreateDate') },
keyword: { label: t('products:Title'), col: 4 },
@ -121,14 +120,14 @@ const Extras = ({ productId, onChange, ...props }) => {
const handleNewAddOn = async (item) => {
setExtrasData(prev => [].concat(prev, [item]));
// todo: ;
const _item = pick(item.info, ['id', 'title', 'code']);
const _item = pick(item, ['id', 'title', 'code']);
const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [_item] });
newSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`);
await handleGetAgencyProductExtras();
}
const handleDelAddon = async (item) => {
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [item.info.id] });
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [item.id] });
delSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`);
await handleGetAgencyProductExtras();
};

Loading…
Cancel
Save