perf: 使用 prettier 格式化代码;优化搜索组件

main
Jimmy Liow 9 months ago
parent f0be26199f
commit 58247055f1

@ -0,0 +1,8 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false
}

@ -1,22 +1,24 @@
import { Outlet, Link, NavLink } from 'react-router-dom';
import { Layout, Menu, ConfigProvider, theme, Row, Col, Typography, Flex, App as AntApp } from 'antd';
import 'antd/dist/reset.css';
import AppLogo from '@/assets/logo-gh.png';
import 'dayjs/locale/zh-cn';
import ErrorBoundary from '@/components/ErrorBoundary';
import { BUILD_VERSION, } from '@/config';
import { useThemeContext } from '@/stores/ThemeContext';
import { Outlet, Link, NavLink } from 'react-router-dom'
import {
Layout,
Menu,
ConfigProvider,
theme,
Row,
Col, App as AntApp
} from 'antd'
import 'antd/dist/reset.css'
import AppLogo from '@/assets/logo-gh.png'
import 'dayjs/locale/zh-cn'
import ErrorBoundary from '@/components/ErrorBoundary'
import { BUILD_VERSION } from '@/config'
import { useThemeContext } from '@/stores/ThemeContext'
const { Header, Content, Footer } = Layout;
const { Title } = Typography;
const { Header, Content, Footer } = Layout
function App() {
const { colorPrimary } = useThemeContext()
console.info('theme: ', theme)
return (
<ConfigProvider
theme={{
@ -24,33 +26,44 @@ function App() {
colorPrimary: colorPrimary,
},
algorithm: theme.defaultAlgorithm,
}}>
}}
>
<AntApp>
<ErrorBoundary>
<Layout className='min-h-screen'>
<Header className='sticky top-0 z-10 w-full'>
<Row gutter={{ md: 24 }} justify='start' align='middle'>
<Layout className="min-h-screen">
<Header className="sticky top-0 z-10 w-full">
<Row gutter={{ md: 24 }} justify="start" align="middle">
<Col span={15}>
<NavLink to='/'>
<img src={AppLogo} className='float-left h-9 my-4 mr-6 ml-0 bg-white/30' alt='App logo' />
<NavLink to="/">
<img
src={AppLogo}
className="float-left h-9 my-4 mr-6 ml-0 bg-white/30"
alt="App logo"
/>
</NavLink>
<Menu
theme='dark'
mode='horizontal'
theme="dark"
mode="horizontal"
selectedKeys={['hotel']}
items={[
{ key: 'hotel', label: <Link to='/hotel/list'>喜玩酒店</Link> }
{
key: 'hotel',
label: <Link to="/hotel/list">喜玩酒店</Link>,
},
]}
/>
</Col>
</Row>
</Header>
<Content className='p-6 m-0 min-h-72 bg-white flex justify-center'>
<div className='max-w-5xl w-full'>
<Content className="p-6 m-0 min-h-72 bg-white flex justify-center">
<div className="max-w-5xl w-full">
<Outlet />
</div>
</Content>
<Footer>China Highlights International Travel Service Co., LTD, Version: {BUILD_VERSION}</Footer>
<Footer>
China Highlights International Travel Service Co., LTD, Version:{' '}
{BUILD_VERSION}
</Footer>
</Layout>
</ErrorBoundary>
</AntApp>

@ -2,29 +2,27 @@ import { useState, useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { Modal, InputNumber, Form, Typography, DatePicker, Button } from 'antd'
import useHotelStore from '@/stores/Hotel'
import { RoomList } from "./HotelComponents"
import { RoomList } from './HotelComponents'
import dayjs from 'dayjs'
import { isEmpty } from '@/utils/commons'
function Detail() {
const { hotelId, checkin, checkout } = useParams()
const [loading, setLoading] = useState(true)
const [hotelQuotation, setHotelQuotation] = useState({
hotelName: '',
roomName: '',
price: ''
price: '',
})
const navigate = useNavigate()
const [searchForm] = Form.useForm()
const [selectedHotel, getRoomListByHotel, roomList] =
useHotelStore(state =>
[state.selectedHotel, state.getRoomListByHotel, state.roomList])
useEffect (() => {
const [selectedHotel, getRoomListByHotel, roomList] = useHotelStore(
(state) => [state.selectedHotel, state.getRoomListByHotel, state.roomList],
)
useEffect(() => {
// http://localhost:5175/hotel/416980/2024-09-10/2024-09-11
if (isEmpty(hotelId) || isEmpty(checkin) || isEmpty(checkout)) {
console.info('criteria is null')
@ -33,13 +31,13 @@ function Detail() {
searchForm.setFieldsValue({
dataRange: [dayjs(checkin), dayjs(checkout)],
adultCount: 2,
roomCount: 1
roomCount: 1,
})
setLoading(true)
getRoomListByHotel(hotelId, checkin, checkout)
.finally(() => setLoading(false))
getRoomListByHotel(hotelId, checkin, checkout).finally(() =>
setLoading(false),
)
}
}, [])
const handelFormFinish = () => {
@ -51,18 +49,20 @@ function Detail() {
// })
setLoading(true)
getRoomListByHotel(hotelId,
getRoomListByHotel(
hotelId,
formValue.dataRange[0].format('YYYY-MM-DD'),
formValue.dataRange[1].format('YYYY-MM-DD'),
formValue.adultCount, formValue.roomCount)
.finally(() => setLoading(false))
formValue.adultCount,
formValue.roomCount,
).finally(() => setLoading(false))
}
const handleRoomChange = (room, plan) => {
const forHtJson = {
hotelName: selectedHotel.hotel_name,
roomName: room.RoomName,
price: plan.Price
price: plan.Price,
}
setHotelQuotation(forHtJson)
showModal()
@ -82,28 +82,32 @@ function Detail() {
return (
<div>
<input type='hidden' id='forHtJson' />
<Modal title='你选择了' open={isModalOpen} onOk={handleOk} onCancel={handleCancel} footer={null}>
<input type="hidden" id="forHtJson" />
<Modal
title="你选择了"
open={isModalOpen}
onOk={handleOk}
onCancel={handleCancel}
footer={null}
>
<p>酒店{hotelQuotation.hotelName}</p>
<p>房型{hotelQuotation.roomName}</p>
<p>价格{hotelQuotation.price}</p>
</Modal>
<Typography.Title level={4}>
{selectedHotel.hotel_name}
</Typography.Title>
<div className='p-2'>
<Typography.Title level={4}>{selectedHotel.hotel_name}</Typography.Title>
<div className="p-2">
<Form
name='searchForm'
name="searchForm"
form={searchForm}
layout='inline'
layout="inline"
onFinish={handelFormFinish}
autoComplete='off'
autoComplete="off"
>
<Form.Item
label={'入住时间'}
name='dataRange'
allowClear={false}
name="dataRange"
allowclear="false"
rules={[
{
required: true,
@ -113,27 +117,29 @@ function Detail() {
>
<DatePicker.RangePicker placeholder={['入住日期', '退房日期']} />
</Form.Item>
<Form.Item
label={'房间数'}
name='roomCount'
>
<Form.Item label={'房间数'} name="roomCount">
<InputNumber min={1} max={100} />
</Form.Item>
<Form.Item
label={'人数'}
name='adultCount'
>
<Form.Item label={'人数'} name="adultCount">
<InputNumber min={1} max={100} />
</Form.Item>
<Form.Item>
<Button type='primary' htmlType='submit'>搜索</Button>
<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>
)
}

@ -1,102 +1,56 @@
import { createContext, useContext, useState } from 'react'
import { Flex, Button, Image, Divider, Typography, Empty, Skeleton, Row, Col, InputNumber, Form, DatePicker, Input } from 'antd'
import { createContext, useContext } from 'react'
import {
Flex,
Button,
Image,
Divider,
Typography,
Empty,
Skeleton,
Row,
Col,
} from 'antd'
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 }) {
if (dataSource && dataSource.length > 0) {
const itemList = dataSource.map((data, index) => {
return (
<>
<Skeleton active loading={loading} key={index}>
<Row gutter={16} className='rounded shadow-md hover:shadow-lg hover:shadow-gray-400 p-2 border-solid border border-gray-100'>
<Row
gutter={16}
className="rounded shadow-md hover:shadow-lg hover:shadow-gray-400 p-2 border-solid border border-gray-100"
>
<Col span={24}>
<Flex
vertical gap='small'
align='flex-start'
justify='flex-start'
vertical
gap="small"
align="flex-start"
justify="flex-start"
>
<Typography.Title level={5}>{data.hotel_name}</Typography.Title>
<Typography.Text type="secondary">
{data.address}
</Typography.Text>
<span>{data.base_price.Price ?? 0} </span>
<Button
size="small"
type="primary"
onClick={() => onChange(data)}
>
<Typography.Title level={5}>
{data.hotel_name}
</Typography.Title>
<Typography.Text type='secondary'>{data.address}</Typography.Text>
<span>{data.base_price.Price??0} </span>
<Button size='small' type='primary' onClick={() => onChange(data)}>
查看房型
</Button>
</Flex>
</Col>
</Row>
</Skeleton>
</>
)
})
return (
<HotelContext.Provider value={{ onChange }}>
<Flex vertical gap='small'>
<Flex vertical gap="small">
{itemList}
</Flex>
</HotelContext.Provider>
@ -107,23 +61,20 @@ export function HotelList({ dataSource, loading, onChange }) {
}
export function RoomList({ dataSource, loading, onChange }) {
const triggerChange = (changedValue) => {
onChange?.(
changedValue
)
onChange?.(changedValue)
}
if (dataSource && dataSource.length > 0) {
const itemList = dataSource.map((room, index) => {
let roomImage = <Empty description={false}/>
let roomImage = <Empty description={false} />
if (room?.Images && room?.Images.length > 0) {
roomImage = <Image src={room?.Images[0].Url} />
}
return (
<Skeleton active loading={loading} key={index}>
<div className='rounded shadow-md hover:shadow-lg hover:shadow-gray-400 p-2 border-solid border border-gray-100'>
<div className="rounded shadow-md hover:shadow-lg hover:shadow-gray-400 p-2 border-solid border border-gray-100">
<Typography.Title level={5}>
{room.RoomName}, {room.BedTypeDesc}
</Typography.Title>
@ -131,17 +82,15 @@ export function RoomList({ dataSource, loading, onChange }) {
<Col span={4}>
<Flex vertical>
{roomImage}
<span>大小: {room.Area??0}</span>
<span>大小: {room.Area ?? 0}</span>
</Flex>
</Col>
<Col span={20}>
{
room.RatePlans.map((plan, index) => {
{room.RatePlans.map((plan, index) => {
return (
<PlanItem key={index} room={room} plan={plan}></PlanItem>
)
})
}
})}
</Col>
</Row>
</div>
@ -151,7 +100,7 @@ export function RoomList({ dataSource, loading, onChange }) {
return (
<HotelContext.Provider value={{ triggerChange }}>
<Flex gap='middle' vertical>
<Flex gap="middle" vertical>
{itemList}
</Flex>
</HotelContext.Provider>
@ -164,7 +113,8 @@ export function RoomList({ dataSource, loading, onChange }) {
const getMealDesc = (plan) => {
const type = plan.MealType
let desc = '未知'
if (type === 1) desc = '早' + plan.Breakfast + '中' + plan.Lunch + '晚' + plan.Dinner
if (type === 1)
desc = '早' + plan.Breakfast + '中' + plan.Lunch + '晚' + plan.Dinner
else if (type === 2) desc = '半包'
else if (type === 3) desc = '全包'
else if (type === 4) desc = '午/晚二选一'
@ -173,20 +123,30 @@ const getMealDesc = (plan) => {
return desc
}
const PlanItem = ({room, plan}) => {
const PlanItem = ({ room, plan }) => {
const { triggerChange } = useContext(HotelContext)
return (
<>
<Row gutter={[16, 16]} className=' divide-y divide-slate-700'>
<Col span={6}><span>: {getMealDesc(plan)}</span></Col>
<Col span={6}><span>{plan.Cancelable ? plan.CancelRulesText.join('') : plan.CancelableText}</span></Col>
<Row gutter={[16, 16]} className=" divide-y divide-slate-700">
<Col span={6}>
<span>: {getMealDesc(plan)}</span>
</Col>
<Col span={6}>
<span>
{plan.Cancelable
? plan.CancelRulesText.join('')
: plan.CancelableText}
</span>
</Col>
<Col span={6}>
<b>{plan.PriceUnit}</b>
<small>{plan.Currency}</small>
</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>
<Divider />
</>

@ -1,19 +1,20 @@
import { useState, useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { Space, Typography } from 'antd'
import { Space, Typography, Form, Button, Input, DatePicker } from 'antd'
import useHotelStore from '@/stores/Hotel'
import dayjs from 'dayjs'
import { HotelList, SearchForm } from './HotelComponents'
import { HotelList } from './HotelComponents'
import { isEmpty } from '@/utils/commons'
const { Title } = Typography
const List = () => {
const [loading, setLoading] = useState(false)
let searchForm = null
const [searchByCriteria, hotelList, selectHotel] =
useHotelStore(state =>
[state.searchByCriteria, state.hotelList, state.selectHotel])
const [searchForm] = Form.useForm()
const [searchByCriteria, hotelList, selectHotel] = useHotelStore((state) => [
state.searchByCriteria,
state.hotelList,
state.selectHotel,
])
const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams()
@ -25,46 +26,101 @@ const List = () => {
useEffect(() => {
// Hotel Skypark Kingstown Dongdaemun
// http://localhost:5175/hotel/list?hotel=s&checkin=2024-8-20&checkout=2024-8-21
if (isEmpty(hotelName) || isEmpty(checkinDateString) || isEmpty(checkoutDateString)) {
console.info('criteria is null')
if (
isEmpty(hotelName) ||
isEmpty(checkinDateString) ||
isEmpty(checkoutDateString)
) {
console.error('criteria is null')
} else {
searchForm.setFieldsValue({
dataRange: [dayjs(checkinDateString), dayjs(checkoutDateString)],
hotelName: hotelName
hotelName: hotelName,
})
setLoading(true)
searchByCriteria(searchForm.getFieldValue())
.finally(() => setLoading(false))
searchByCriteria(searchForm.getFieldValue()).finally(() =>
setLoading(false),
)
}
}, [])
const handelSearch = (formValue) => {
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')
checkout: formValue.dataRange[1].format('YYYY-MM-DD'),
})
setLoading(true)
searchByCriteria(formValue)
.finally(() => setLoading(false))
searchByCriteria(formValue).finally(() => setLoading(false))
}
const handleHotelChange = (hotel) => {
selectHotel(hotel)
navigate(`/hotel/${hotel.hotel_id}/${searchForm.getFieldValue().dataRange[0].format('YYYY-MM-DD')}/${searchForm.getFieldValue().dataRange[1].format('YYYY-MM-DD')}`)
navigate(
`/hotel/${hotel.hotel_id}/${searchForm
.getFieldValue()
.dataRange[0].format('YYYY-MM-DD')}/${searchForm
.getFieldValue()
.dataRange[1].format('YYYY-MM-DD')}`,
)
}
const SearchForm = () => {
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>
)
}
return (
<>
<Space direction='vertical' className='w-full'>
<Space direction="vertical" className="w-full">
<Title level={3}>酒店列表</Title>
<SearchForm
onSearch={handelSearch}
onInit={form => searchForm = form}
>
</SearchForm>
<HotelList loading={loading} dataSource={hotelList} onChange={h => handleHotelChange(h)}></HotelList>
<SearchForm></SearchForm>
<HotelList
loading={loading}
dataSource={hotelList}
onChange={(h) => handleHotelChange(h)}
></HotelList>
</Space>
</>
)

Loading…
Cancel
Save