Merge branch 'feature/price_manager' of github.com:hainatravel/GHHub into feature/price_manager

feature/price_manager
黄文强@HWQ-PC 1 year ago
commit 8fd197a3e0

@ -14,13 +14,6 @@
100%{-webkit-transform:translate(150px)} 100%{-webkit-transform:translate(150px)}
} }
</style> </style>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-7JN1HT1DY4"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-7JN1HT1DY4');
</script>
</head> </head>
<body> <body>
<div id="root"> <div id="root">

@ -39,6 +39,7 @@
"Login": "Login", "Login": "Login",
"Username": "Username", "Username": "Username",
"Realname": "Realname",
"Password": "Password", "Password": "Password",
"ChangePassword": "Change password", "ChangePassword": "Change password",

@ -39,6 +39,7 @@
"Login": "登录", "Login": "登录",
"Username": "账号", "Username": "账号",
"Realname": "姓名",
"Password": "密码", "Password": "密码",
"ChangePassword": "修改密码", "ChangePassword": "修改密码",

@ -21,3 +21,6 @@
justify-content: center; justify-content: center;
width: 100%; width: 100%;
} }
.ant-table-wrapper.border-collapse table {
border-collapse: collapse;
}

@ -51,10 +51,10 @@ export const useProductsTypes = () => {
{ label: t('products:type.UltraService'), value: 'B', key: 'B' }, { label: t('products:type.UltraService'), value: 'B', key: 'B' },
{ label: t('products:type.Car'), value: 'J', key: 'J' }, { label: t('products:type.Car'), value: 'J', key: 'J' },
{ label: t('products:type.Guide'), value: 'Q', key: 'Q' }, { label: t('products:type.Guide'), value: 'Q', key: 'Q' },
{ label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路
{ label: t('products:type.Attractions'), value: '7', key: '7' }, { label: t('products:type.Attractions'), value: '7', key: '7' },
{ label: t('products:type.Meals'), value: 'R', key: 'R' }, { label: t('products:type.Meals'), value: 'R', key: 'R' },
{ label: t('products:type.Extras'), value: '8', key: '8' }, { label: t('products:type.Extras'), value: '8', key: '8' },
{ label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路
]; ];
setTypes(newData); setTypes(newData);
}, [i18n.language]); }, [i18n.language]);

@ -1,5 +1,6 @@
import { create } from 'zustand' import { create } from 'zustand'
import { fetchJSON, postForm } from '@/utils/request' import { fetchJSON, postForm } from '@/utils/request'
import { isEmpty } from '@/utils/commons'
import { HT_HOST } from "@/config" import { HT_HOST } from "@/config"
import { usingStorage } from '@/hooks/usingStorage' import { usingStorage } from '@/hooks/usingStorage'
@ -147,6 +148,7 @@ const useAccountStore = create((set, get) => ({
const resultArray = await fetchAccountList(searchParams) const resultArray = await fetchAccountList(searchParams)
console.info(resultArray)
const mapAccoutList = resultArray.map((r) => { const mapAccoutList = resultArray.map((r) => {
return { return {
accountId: r.wu_id, accountId: r.wu_id,
@ -160,7 +162,7 @@ const useAccountStore = create((set, get) => ({
travelAgencyId: r.travel_agency_id, travelAgencyId: r.travel_agency_id,
disabled: r.wu_limitsign, disabled: r.wu_limitsign,
// 数据库支持逗号分隔多角色(5,6,7),目前界面只需单个。 // 数据库支持逗号分隔多角色(5,6,7),目前界面只需单个。
roleId: parseInt(r.roles), roleId: isEmpty(r.roles) ? 0 : parseInt(r.roles),
role: r.roles_name, role: r.roles_name,
} }
}) })

@ -43,8 +43,18 @@ async function fetchLastRequet() {
return errcode !== 0 ? {} : result return errcode !== 0 ? {} : result
} }
const initialState = {
tokenInterval: null,
tokenTimeout: true,
loginStatus: 0,
defaltRoute: '',
permissionList: []
}
const useAuthStore = create(lifecycleware((set, get) => ({ const useAuthStore = create(lifecycleware((set, get) => ({
...initialState,
onAuth: async () => { onAuth: async () => {
const { startTokenInterval, loadUserPermission } = get() const { startTokenInterval, loadUserPermission } = get()
const { userId, loginToken } = usingStorage() const { userId, loginToken } = usingStorage()
@ -98,16 +108,11 @@ const useAuthStore = create(lifecycleware((set, get) => ({
const { clearStorage } = usingStorage() const { clearStorage } = usingStorage()
clearStorage() clearStorage()
clearInterval(tokenInterval) clearInterval(tokenInterval)
set(() => ({ set(initialState)
defaultRoute: '/',
loginStatus: 0,
tokenInterval: null,
tokenTimeout: true
}))
}, },
startTokenInterval: () => { startTokenInterval: () => {
const { loginTimeout } = get() const { logout } = get()
async function checkTokenTimeout() { async function checkTokenTimeout() {
const { LastReqDate } = await fetchLastRequet() const { LastReqDate } = await fetchLastRequet()
@ -116,27 +121,17 @@ const useAuthStore = create(lifecycleware((set, get) => ({
const diffTime = now.getTime() - lastReqDate.getTime() const diffTime = now.getTime() - lastReqDate.getTime()
const diffHours = diffTime/1000/60/60 const diffHours = diffTime/1000/60/60
if (diffHours > 1) { if (diffHours > 1) {
loginTimeout() logout()
} }
} }
const interval = setInterval(() => checkTokenTimeout(), 1000*60*20) const interval = setInterval(() => checkTokenTimeout(), 1000*60*10)
set(() => ({ set(() => ({
tokenInterval: interval tokenInterval: interval
})) }))
}, },
loginTimeout: () => { // TODO: 迁移到 Account.js
const { tokenInterval } = get()
const { clearStorage } = usingStorage()
clearStorage()
clearInterval(tokenInterval)
set(() => ({
tokenTimeout: true
}))
},
// 迁移到 Account.js
changeUserPassword: (password, newPassword) => { changeUserPassword: (password, newPassword) => {
const { userId } = usingStorage() const { userId } = usingStorage()
const formData = new FormData(); const formData = new FormData();
@ -174,16 +169,6 @@ const useAuthStore = create(lifecycleware((set, get) => ({
}) })
}, },
tokenInterval: null,
tokenTimeout: false,
loginStatus: 0,
defaltRoute: '',
permissionList: [],
}))) })))
export default useAuthStore export default useAuthStore

@ -27,18 +27,18 @@ export const getAgencyProductsAction = async (param) => {
}; };
/** /**
* todo: *
*/ */
export const addProductExtraAction = async (body) => { export const addProductExtraAction = async (body) => {
const { errcode, result } = await postJSON(`${HT_HOST}/products/extras`, body); const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/products_extras_add`, body);
return errcode === 0 ? true : false; return errcode === 0 ? true : false;
}; };
/** /**
* todo: *
*/ */
export const delProductExtrasAction = async (body) => { export const delProductExtrasAction = async (body) => {
const { errcode, result } = await postJSON(`${HT_HOST}/products/extras/del`, body); const { errcode, result } = await postJSON(`${HT_HOST}/Service_BaseInfoWeb/products_extras_del`, body);
return errcode === 0 ? true : false; return errcode === 0 ? true : false;
}; };

@ -48,6 +48,7 @@ function App() {
.then(u => { .then(u => {
setUserDetail({ setUserDetail({
username: u.LoginName, username: u.LoginName,
realname: u.real_name,
travelAgencyName: u.VName, travelAgencyName: u.VName,
}) })
}) })
@ -90,6 +91,8 @@ function App() {
theme={{ theme={{
token: { token: {
colorPrimary: '#00b96b', colorPrimary: '#00b96b',
// "sizeStep": 3,
// "sizeUnit": 3,
}, },
algorithm: theme.defaultAlgorithm, algorithm: theme.defaultAlgorithm,
}}> }}>
@ -158,7 +161,6 @@ function App() {
items: [...[ items: [...[
{ label: <Link to='/account/change-password'>{t('ChangePassword')}</Link>, key: '0' }, { label: <Link to='/account/change-password'>{t('ChangePassword')}</Link>, key: '0' },
{ label: <Link to='/account/profile'>{t('Profile')}</Link>, key: '1' }, { label: <Link to='/account/profile'>{t('Profile')}</Link>, key: '1' },
{ type: 'divider' },
isPermitted(PERM_ACCOUNT_MANAGEMENT) ? { label: <Link to='/account/management'>{t('account:accountList')}</Link>, key: '3' } : null, isPermitted(PERM_ACCOUNT_MANAGEMENT) ? { label: <Link to='/account/management'>{t('account:accountList')}</Link>, key: '3' } : null,
isPermitted(PERM_ROLE_NEW) ? { label: <Link to='/account/role-list'>{t('account:roleList')}</Link>, key: '4' } : null, isPermitted(PERM_ROLE_NEW) ? { label: <Link to='/account/role-list'>{t('account:roleList')}</Link>, key: '4' } : null,
{ type: 'divider' }, { type: 'divider' },
@ -170,7 +172,7 @@ function App() {
> >
<a onClick={e => e.preventDefault()}> <a onClick={e => e.preventDefault()}>
<Space> <Space>
{userDetail?.username} {userDetail?.realname}
<DownOutlined /> <DownOutlined />
</Space> </Space>
</a> </a>

@ -81,13 +81,15 @@ function Management() {
useEffect(() => { useEffect(() => {
fetchRoleList() fetchRoleList()
.then((roleList) => { .then((roleList) => {
setRoleAllList(roleList.map(r => { const roleListMap = roleList.map(r => {
return { return {
value: r.role_id, value: r.role_id,
label: r.role_name, label: r.role_name,
disabled: r.role_id === 1 disabled: r.role_id === 1
} }
})) })
roleListMap.unshift({ value: 0, label: '未设置', disabled: true });
setRoleAllList(roleListMap)
}) })
}, []) }, [])

@ -15,7 +15,8 @@ function Profile() {
.then(json => { .then(json => {
setUserDetail({ setUserDetail({
username: json.LoginName, username: json.LoginName,
telephone: json.LkPhone, realname: json.real_name,
rolesName: json.roles_name,
emailAddress: json.LMI_listmail, emailAddress: json.LMI_listmail,
travelAgencyName: json.VName, travelAgencyName: json.VName,
}) })
@ -28,7 +29,7 @@ function Profile() {
<Col span={12} offset={6}> <Col span={12} offset={6}>
<Descriptions title={t('userProfile')} layout="vertical" column={2}> <Descriptions title={t('userProfile')} layout="vertical" column={2}>
<Descriptions.Item label={t("Username")}>{userDetail?.username}</Descriptions.Item> <Descriptions.Item label={t("Username")}>{userDetail?.username}</Descriptions.Item>
<Descriptions.Item label={t("Telephone")}>{userDetail?.telephone}</Descriptions.Item> <Descriptions.Item label={t("Realname")}>{userDetail?.realname}({userDetail?.rolesName})</Descriptions.Item>
<Descriptions.Item label={t("Email")}>{userDetail?.emailAddress}</Descriptions.Item> <Descriptions.Item label={t("Email")}>{userDetail?.emailAddress}</Descriptions.Item>
<Descriptions.Item label={t("Company")}>{userDetail?.travelAgencyName}</Descriptions.Item> <Descriptions.Item label={t("Company")}>{userDetail?.travelAgencyName}</Descriptions.Item>
</Descriptions> </Descriptions>

@ -56,7 +56,7 @@ const Header = ({ title, agency, refresh, ...props }) => {
); );
}; };
const PriceTable = ({ dataSource, refresh }) => { const PriceTable = ({ productType, dataSource, refresh }) => {
const { t } = useTranslation('products'); const { t } = useTranslation('products');
const [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]); const [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]);
const { message, notification } = App.useApp(); const { message, notification } = App.useApp();
@ -84,8 +84,16 @@ const PriceTable = ({ dataSource, refresh }) => {
}); });
}; };
const rowStyle = (r, tri) => {
const trCls = tri%2 !== 0 ? ' bg-stone-50' : '';
const [infoI, quoteI] = r.rowSpanI;
const bigTrCls = quoteI === 0 && tri !== 0 ? 'border-collapse border-double border-0 border-t-4 border-stone-300' : '';
return [trCls, bigTrCls].join(' ');
};
const columns = [ const columns = [
{ key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan }), render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.title || '' }, { key: 'title', dataIndex: ['info', 'title'], width: '16rem', title: t('Title'), onCell: (r, index) => ({ rowSpan: r.rowSpan, }), className: 'bg-white', render: (text, r) => text || r.lgc_details?.['2']?.title || r.lgc_details?.['1']?.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}`)}` }, { key: 'adult', title: t('AgeType.Adult'), render: (_, { adult_cost, currency, unit_id, unit_name }) => `${adult_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` },
{ key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_id, unit_name }) => `${child_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` }, { key: 'child', title: t('AgeType.Child'), render: (_, { child_cost, currency, unit_id, unit_name }) => `${child_cost} ${currency} / ${t(`PriceUnit.${unit_id}`)}` },
// {key: 'unit', title: t('Unit'), }, // {key: 'unit', title: t('Unit'), },
@ -121,7 +129,7 @@ const PriceTable = ({ dataSource, refresh }) => {
) : null, ) : null,
}, },
]; ];
return <Table size={'small'} pagination={false} {...{ columns, dataSource }} rowKey={(r) => r.id} />; return <Table size={'small'} className='border-collapse' rowHoverable={false} rowClassName={rowStyle} pagination={false} {...{ columns, dataSource }} rowKey={(r) => r.id} />;
}; };
/** /**
@ -145,8 +153,9 @@ const TypesPanels = (props) => {
children: ( children: (
<PriceTable <PriceTable
// loading={loading} // loading={loading}
productType={ele.value}
dataSource={agencyProducts[ele.value].reduce( dataSource={agencyProducts[ele.value].reduce(
(r, c) => (r, c, ri) =>
r.concat( r.concat(
c.quotation.map((q, i) => ({ c.quotation.map((q, i) => ({
...q, ...q,
@ -158,6 +167,7 @@ const TypesPanels = (props) => {
info: c.info, info: c.info,
lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}), lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}),
rowSpan: i === 0 ? c.quotation.length : 0, rowSpan: i === 0 ? c.quotation.length : 0,
rowSpanI: [ri, i],
})) }))
), ),
[] []

@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { App, Table, Button, Modal, Popconfirm } from 'antd'; import { App, Table, Button, Modal, Popconfirm } from 'antd';
import { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index'; import { getAgencyProductExtrasAction, getAgencyProductsAction, addProductExtraAction, delProductExtrasAction } from '@/stores/Products/Index';
import { cloneDeep } from '@/utils/commons'; import { cloneDeep, pick } from '@/utils/commons';
import SearchForm from '@/components/SearchForm'; import SearchForm from '@/components/SearchForm';
import RequireAuth from '@/components/RequireAuth'; import RequireAuth from '@/components/RequireAuth';
@ -46,7 +46,7 @@ const NewAddonModal = ({ onPick, ...props }) => {
title: t('products:price'), title: t('products:price'),
dataIndex: ['quotation', '0', 'adult_cost'], dataIndex: ['quotation', '0', 'adult_cost'],
width: '10rem', width: '10rem',
render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`, // todo: render: (_, { quotation }) => `${quotation[0].adult_cost} ${quotation[0].currency} / ${quotation[0].unit_name}`,
}, },
{ {
key: 'action', key: 'action',
@ -120,15 +120,15 @@ const Extras = ({ productId, onChange, ...props }) => {
const handleNewAddOn = async (item) => { const handleNewAddOn = async (item) => {
setExtrasData(prev => [].concat(prev, [item])); setExtrasData(prev => [].concat(prev, [item]));
// todo: ; // todo: ;
const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [2] }); const _item = pick(item.info, ['id', 'title', 'code']);
newSuccess ? message.success(t('Action')+t('Success')) : message.error(t('Action')+t('Failed')); const newSuccess = await addProductExtraAction({ travel_agency_id, id: productId, extras: [_item] });
newSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`);
await handleGetAgencyProductExtras(); await handleGetAgencyProductExtras();
} }
const handleDelAddon = async (item) => { const handleDelAddon = async (item) => {
// todo: const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [item.info.id] });
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [2] }); delSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`);
delSuccess ? message.success(t('Action')+t('Success')) : message.error(t('Action')+t('Failed'));
await handleGetAgencyProductExtras(); await handleGetAgencyProductExtras();
}; };

Loading…
Cancel
Save