a. 房型列表展示搜索条件(日期、酒店名、房间数、人数)
    c. 取消条款展示详细内容
    d. 房型价格改为显示单价
main
Jimmy Liow 10 months ago
parent 9646449069
commit a758629a8d

@ -11,10 +11,10 @@ export const fetchHotelList = async (hotelName, checkinDateString, checkoutDateS
return errcode !== 0 ? {} : data return errcode !== 0 ? {} : data
} }
export const fetchAvailability = async (hotelId, checkinDateString, checkoutDateString) => { export const fetchAvailability = async (hotelId, checkinDateString, checkoutDateString, adultCount=2, roomCount=1) => {
const { errcode, data } = await fetchJSON( const { errcode, data } = await fetchJSON(
'http://202.103.68.93:3002/availability', 'http://202.103.68.93:3002/availability',
{ hotel_id: hotelId, checkin: checkinDateString, checkout: checkoutDateString } { hotel_id: hotelId, checkin: checkinDateString, checkout: checkoutDateString, adults: adultCount, rooms: roomCount }
) )
return errcode !== 0 ? {} : data return errcode !== 0 ? {} : data
} }
@ -47,11 +47,13 @@ const useHotelStore = create(devtools((set, get) => ({
})) }))
}, },
getRoomListByHotel: async(hotelId, checkin, checkout) => { getRoomListByHotel: async(hotelId, checkin, checkout, adultCount=2, roomCount=1) => {
const resultArray = await fetchAvailability( const resultArray = await fetchAvailability(
hotelId, hotelId,
checkin, checkin,
checkout checkout,
adultCount,
roomCount
) )
console.info(resultArray) console.info(resultArray)

@ -46,7 +46,7 @@ function App() {
</Row> </Row>
</Header> </Header>
<Content className='p-6 m-0 min-h-72 bg-white flex justify-center'> <Content className='p-6 m-0 min-h-72 bg-white flex justify-center'>
<div className='max-w-3xl'> <div className='max-w-6xl'>
<Outlet /> <Outlet />
</div> </div>
</Content> </Content>

@ -1,9 +1,11 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom' import { useParams, useNavigate } from 'react-router-dom'
import { Row, Col, Modal, Space, Form, Typography, DatePicker, Input, Button, App } from 'antd' import { Row, Col, Modal, InputNumber, Form, Typography, DatePicker, Input, Button, App } from 'antd'
import useHotelStore from '@/stores/Hotel' import useHotelStore from '@/stores/Hotel'
import { HotelList, RoomList } from "./HotelComponents"; import { HotelList, RoomList } from "./HotelComponents";
import { ExclamationCircleFilled } from '@ant-design/icons' import { ExclamationCircleFilled } from '@ant-design/icons'
import dayjs from 'dayjs'
import { isEmpty } from '@/utils/commons'
const { Title } = Typography const { Title } = Typography
function Detail() { function Detail() {
@ -18,6 +20,7 @@ function Detail() {
}) })
const navigate = useNavigate() const navigate = useNavigate()
const [searchForm] = Form.useForm()
const { notification, modal } = App.useApp() const { notification, modal } = App.useApp()
const [selectedHotel, getRoomListByHotel, roomList] = const [selectedHotel, getRoomListByHotel, roomList] =
useHotelStore(state => useHotelStore(state =>
@ -25,11 +28,34 @@ function Detail() {
useEffect (() => { useEffect (() => {
// http://localhost:5175/hotel/416980/2024-09-10/2024-09-11
if (isEmpty(hotelId) || isEmpty(checkin) || isEmpty(checkout)) {
console.info('criteria is null')
} else {
searchForm.setFieldsValue({
dataRange: [dayjs(checkin), dayjs(checkout)],
adultCount: 2,
roomCount: 1
})
setLoading(true)
getRoomListByHotel(hotelId, checkin, checkout)
.finally(() => setLoading(false))
}
}, [])
const handelFormFinish = () => {
const formValue = searchForm.getFieldValue()
// setSearchParams({
// hotel: formValue.hotelName,
// checkin: formValue.dataRange[0].format('YYYY-MM-DD'),
// checkout: formValue.dataRange[1].format('YYYY-MM-DD')
// })
setLoading(true) setLoading(true)
getRoomListByHotel(hotelId, checkin, checkout) getRoomListByHotel(hotelId, checkin, checkout, formValue.adultCount, formValue.roomCount)
.finally(() => setLoading(false)) .finally(() => setLoading(false))
}, []) }
const handleRoomChange = (room, plan) => { const handleRoomChange = (room, plan) => {
console.info('room: ', room) console.info('room: ', room)
@ -60,26 +86,62 @@ function Detail() {
} }
return ( return (
<> <div className='min-w-[1152px]'>
<input type='hidden' id='forHtJson' /> <input type='hidden' id='forHtJson' />
<Modal title='你选择了' open={isModalOpen} onOk={handleOk} onCancel={handleCancel}> <Modal title='你选择了' open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
<p>酒店{hotelQuotation.hotelName}</p> <p>酒店{hotelQuotation.hotelName}</p>
<p>房型{hotelQuotation.roomName}</p> <p>房型{hotelQuotation.roomName}</p>
<p>价格{hotelQuotation.price}</p> <p>价格{hotelQuotation.price}</p>
</Modal> </Modal>
<Row gutter={16}>
<Col span={20}>
<Typography.Title level={4}> <Typography.Title level={4}>
{selectedHotel.hotel_name} {selectedHotel.hotel_name}
</Typography.Title> </Typography.Title>
</Col> <div className='p-2'>
<Col span={4}> <Form
<Button onClick={() => navigate(-1)}>Back</Button> name='searchForm'
</Col> form={searchForm}
</Row> layout='inline'
onFinish={handelFormFinish}
onFinishFailed={() => console.info('onFinishFailed')}
autoComplete='off'
>
<Form.Item
label={'入住时间'}
name='dataRange'
allowClear={false}
rules={[
{
required: true,
message: '必填',
},
]}
>
<DatePicker.RangePicker placeholder={['入住日期', '退房日期']} />
</Form.Item>
<Form.Item
label={'房间数'}
name='roomCount'
>
<InputNumber min={1} max={100} />
</Form.Item>
<Form.Item
label={'人数'}
name='adultCount'
>
<InputNumber min={1} max={100} />
</Form.Item>
<Form.Item>
<Button type='primary' htmlType='submit'>搜索</Button>
</Form.Item>
<Form.Item>
<Button onClick={() => navigate(-1)}>返回</Button>
</Form.Item>
</Form>
</div>
<RoomList loading={loading} onChange={({room, plan}) => {handleRoomChange(room, plan)}} dataSource={roomList}></RoomList> <RoomList loading={loading} onChange={({room, plan}) => {handleRoomChange(room, plan)}} dataSource={roomList}></RoomList>
</> </div>
); );
} }
export default Detail; export default Detail

@ -1,8 +1,69 @@
import { createContext, useContext, useState } from 'react' import { createContext, useContext, useState } from 'react'
import { Flex, Button, Image, Typography, Empty, Skeleton, Row, Col } from 'antd' import { Flex, Button, Image, Divider, Typography, Empty, Skeleton, Row, Col, InputNumber, Form, DatePicker, Input } from 'antd'
const HotelContext = createContext() const HotelContext = createContext()
export function SearchForm({ onSearch, onInit }) {
const [searchForm] = Form.useForm()
onInit(searchForm)
const handelFormFinish = () => {
const formValue = searchForm.getFieldValue()
// setSearchParams({
// hotel: formValue.hotelName,
// checkin: formValue.dataRange[0].format('YYYY-MM-DD'),
// checkout: formValue.dataRange[1].format('YYYY-MM-DD')
// })
onSearch(formValue)
// setLoading(true)
// searchByCriteria(formValue)
// .finally(() => setLoading(false))
}
return (
<Form
name='searchForm'
form={searchForm}
layout='inline'
onFinish={handelFormFinish}
onFinishFailed={() => console.info('onFinishFailed')}
autoComplete='off'
>
<Form.Item
label={'酒店名称'}
name='hotelName'
rules={[
{
required: true,
message: '必填',
},
]}
>
<Input placeholder={'不能为空'} />
</Form.Item>
<Form.Item
label={'入住时间'}
name='dataRange'
allowClear={false}
rules={[
{
required: true,
message: '必填',
},
]}
>
<DatePicker.RangePicker placeholder={['入住日期', '退房日期']} />
</Form.Item>
<Form.Item>
<Button type='primary' htmlType='submit'>搜索</Button>
</Form.Item>
</Form>
)
}
export function HotelList({ dataSource, loading, onChange }) { export function HotelList({ dataSource, loading, onChange }) {
if (dataSource && dataSource.length > 0) { if (dataSource && dataSource.length > 0) {
@ -100,18 +161,6 @@ export function RoomList({ dataSource, loading, onChange }) {
} }
} }
const getDeductDesc = (type) => {
let desc = '未知'
if (type === 1) desc = '扣首日'
else if (type === 2) desc = '扣全额'
else if (type === 3) desc = '按价格多少百分比扣'
else if (type === 4) desc = '免费取消'
else if (type === 5) desc = '扣几晚'
else if (type === 6) desc = '扣多少钱'
return desc
}
const getMealDesc = (plan) => { const getMealDesc = (plan) => {
const type = plan.MealType const type = plan.MealType
let desc = '未知' let desc = '未知'
@ -127,18 +176,19 @@ const getMealDesc = (plan) => {
const PlanItem = ({room, plan}) => { const PlanItem = ({room, plan}) => {
const { triggerChange } = useContext(HotelContext) const { triggerChange } = useContext(HotelContext)
return ( return (
<div className='p-2'> <>
<Row gutter={[16, 16]} className=' divide-y divide-slate-700'> <Row gutter={[16, 16]} className=' divide-y divide-slate-700'>
<Col span={6}><span>: {getMealDesc(plan)}</span></Col> <Col span={6}><span>: {getMealDesc(plan)}</span></Col>
<Col span={6}><span>取消: {plan.Cancelable ? plan.CancelRules.map(r => getDeductDesc(r.DeductType)).join(',') : '不'}</span></Col> <Col span={6}><span>{plan.Cancelable ? plan.CancelRulesText.join('') : plan.CancelableText}</span></Col>
<Col span={6}> <Col span={6}>
<b>{plan.Price}</b> <b>{plan.PriceUnit}</b>
<small>{plan.Currency}</small> <small>{plan.Currency}</small>
</Col> </Col>
<Col span={4}><Button size='small' onClick={() => triggerChange({room, plan})}> <Col span={4}><Button size='small' onClick={() => triggerChange({room, plan})}>
选他 选他
</Button></Col> </Button></Col>
</Row> </Row>
</div> <Divider />
</>
) )
} }

@ -1,16 +1,16 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom' import { useNavigate, useSearchParams } from 'react-router-dom'
import { Row, Col, Modal, Space, Form, Typography, DatePicker, Input, Button, App } from 'antd' import { Row, Col, InputNumber, Space, Form, Typography, DatePicker, Input, Button, App } from 'antd'
import useHotelStore from '@/stores/Hotel' import useHotelStore from '@/stores/Hotel'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { HotelList, RoomList } from "./HotelComponents"; import { HotelList, SearchForm } from './HotelComponents'
import { isEmpty } from '@/utils/commons' import { isEmpty } from '@/utils/commons'
const { Title } = Typography const { Title } = Typography
const List = () => { const List = () => {
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [searchForm] = Form.useForm() let searchForm = null
const [searchByCriteria, hotelList, selectHotel] = const [searchByCriteria, hotelList, selectHotel] =
useHotelStore(state => useHotelStore(state =>
[state.searchByCriteria, state.hotelList, state.selectHotel]) [state.searchByCriteria, state.hotelList, state.selectHotel])
@ -25,6 +25,7 @@ const List = () => {
const checkoutDateString = searchParams.get('checkout') const checkoutDateString = searchParams.get('checkout')
useEffect(() => { useEffect(() => {
console.info(searchForm)
// http://localhost:5175/hotel/list?hotel=s&checkin=2024-8-20&checkout=2024-8-21 // http://localhost:5175/hotel/list?hotel=s&checkin=2024-8-20&checkout=2024-8-21
if (isEmpty(hotelName) || isEmpty(checkinDateString) || isEmpty(checkoutDateString)) { if (isEmpty(hotelName) || isEmpty(checkinDateString) || isEmpty(checkoutDateString)) {
console.info('criteria is null') console.info('criteria is null')
@ -39,8 +40,7 @@ const List = () => {
} }
}, []) }, [])
const onSearchFinish = () => { const handelSearch = (formValue) => {
const formValue = searchForm.getFieldValue()
setSearchParams({ setSearchParams({
hotel: formValue.hotelName, hotel: formValue.hotelName,
checkin: formValue.dataRange[0].format('YYYY-MM-DD'), checkin: formValue.dataRange[0].format('YYYY-MM-DD'),
@ -61,42 +61,11 @@ const List = () => {
<> <>
<Space direction='vertical' className='w-full'> <Space direction='vertical' className='w-full'>
<Title level={3}>酒店列表</Title> <Title level={3}>酒店列表</Title>
<Form <SearchForm
name='searchForm' onSearch={handelSearch}
form={searchForm} onInit={form => searchForm = form}
layout='inline'
onFinish={onSearchFinish}
onFinishFailed={() => console.info('onFinishFailed')}
autoComplete='off'
> >
<Form.Item </SearchForm>
label={'入住时间'}
name='dataRange'
rules={[
{
required: true,
message: '必填',
},
]}
>
<DatePicker.RangePicker placeholder={['入住日期', '退房日期']} />
</Form.Item>
<Form.Item
label={'酒店名称'}
name='hotelName'
rules={[
{
required: true,
message: '必填',
},
]}
>
<Input placeholder={'不能为空'} />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">搜索</Button>
</Form.Item>
</Form>
<HotelList loading={loading} dataSource={hotelList} onChange={h => handleHotelChange(h)}></HotelList> <HotelList loading={loading} dataSource={hotelList} onChange={h => handleHotelChange(h)}></HotelList>
</Space> </Space>
</> </>

Loading…
Cancel
Save