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)}
}
</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>
<body>
<div id="root">

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

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

@ -21,3 +21,6 @@
justify-content: center;
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.Car'), value: 'J', key: 'J' },
{ 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.Meals'), value: 'R', key: 'R' },
{ label: t('products:type.Extras'), value: '8', key: '8' },
{ label: t('products:type.Package'), value: 'D', key: 'D' }, // 包价线路
];
setTypes(newData);
}, [i18n.language]);

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

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

@ -27,18 +27,18 @@ export const getAgencyProductsAction = async (param) => {
};
/**
* todo:
*
*/
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;
};
/**
* todo:
*
*/
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;
};

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

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

@ -15,7 +15,8 @@ function Profile() {
.then(json => {
setUserDetail({
username: json.LoginName,
telephone: json.LkPhone,
realname: json.real_name,
rolesName: json.roles_name,
emailAddress: json.LMI_listmail,
travelAgencyName: json.VName,
})
@ -28,7 +29,7 @@ function Profile() {
<Col span={12} offset={6}>
<Descriptions title={t('userProfile')} layout="vertical" column={2}>
<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("Company")}>{userDetail?.travelAgencyName}</Descriptions.Item>
</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 [loading, activeAgency] = useProductsStore((state) => [state.loading, state.activeAgency]);
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 = [
{ 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: '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'), },
@ -121,7 +129,7 @@ const PriceTable = ({ dataSource, refresh }) => {
) : 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: (
<PriceTable
// loading={loading}
productType={ele.value}
dataSource={agencyProducts[ele.value].reduce(
(r, c) =>
(r, c, ri) =>
r.concat(
c.quotation.map((q, i) => ({
...q,
@ -158,6 +167,7 @@ const TypesPanels = (props) => {
info: c.info,
lgc_details: c.lgc_details.reduce((rlgc, clgc) => ({...r, [clgc.lgc]: clgc}), {}),
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 { App, Table, Button, Modal, Popconfirm } from 'antd';
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 RequireAuth from '@/components/RequireAuth';
@ -46,7 +46,7 @@ const NewAddonModal = ({ onPick, ...props }) => {
title: t('products:price'),
dataIndex: ['quotation', '0', 'adult_cost'],
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',
@ -120,15 +120,15 @@ const Extras = ({ productId, onChange, ...props }) => {
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('Action')+t('Success')) : message.error(t('Action')+t('Failed'));
const _item = pick(item.info, ['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) => {
// todo:
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [2] });
delSuccess ? message.success(t('Action')+t('Success')) : message.error(t('Action')+t('Failed'));
const delSuccess = await delProductExtrasAction({ travel_agency_id, id: productId, extras: [item.info.id] });
delSuccess ? message.success(`${t('Success')}`) : message.error(`${t('Failed')}`);
await handleGetAgencyProductExtras();
};

Loading…
Cancel
Save