Merge remote-tracking branch 'origin/dev/2025a' into dev/2025a

dev/ckeditor
Lei OT 3 weeks ago
commit d55d55a3aa

@ -15,31 +15,61 @@ import { useCallback, useEffect, useState } from 'react'
import { import {
Flex,Select,Tooltip, Flex,Select,Tooltip,
Button, Space, Divider, Typography, Button, Space, Divider, Typography,
Input, Radio,Drawer,Checkbox, Input, Radio,Skeleton ,Checkbox,
} from 'antd' } from 'antd'
import { InboxIcon, MailCheckIcon, MailUnreadIcon, SendPlaneFillIcon } from '@/components/Icons' import { InboxIcon, MailCheckIcon, MailUnreadIcon, SendPlaneFillIcon } from '@/components/Icons'
import { useOrderStore, OrderLabelDefaultOptions, OrderStatusDefaultOptions, remindStatusOptions, fetchSetRemindStateAction, remindStatusOptionsMapped } from "@/stores/OrderStore"; import { useOrderStore, OrderLabelDefaultOptions, OrderStatusDefaultOptions, remindStatusOptions, fetchSetRemindStateAction, remindStatusOptionsMapped } from "@/stores/OrderStore";
import { copy, isEmpty } from "@/utils/commons"; import { copy, isEmpty } from "@/utils/commons";
const OrderProfile = ({coliSN, ...props}) => { const OrderProfile = ({coliSN, ...props}) => {
const [loading, setLoading] = useState(false);
const orderLabelOptions = copy(OrderLabelDefaultOptions); const orderLabelOptions = copy(OrderLabelDefaultOptions);
orderLabelOptions.unshift({ value: 0, label: "未设置", disabled: true }); orderLabelOptions.unshift({ value: 0, label: "未设置", disabled: true });
const orderStatusOptions = copy(OrderStatusDefaultOptions); const orderStatusOptions = copy(OrderStatusDefaultOptions);
const [
orderDetail, customerDetail, lastQuotation, quotationList, fetchOrderDetail, setOrderPropValue, appendOrderComment, fetchOtherEmail, otherEmailList, fetchHistoryOrder
] = useOrderStore(s => [
s.orderDetail, s.customerDetail, s.lastQuotation, s.quotationList, s.fetchOrderDetail, s.setOrderPropValue, s.appendOrderComment, s.fetchOtherEmail, s.otherEmailList, s.fetchHistoryOrder
])
useEffect(() => {
if (coliSN) {
setLoading(true);
fetchOrderDetail(coliSN)
.then(result => {
console.info(result)
})
.finally(() => setLoading(false))
// .catch(reason => {
// notification.error({
// message: "",
// description: reason.message,
// placement: "top",
// duration: 60,
// });
// });
}
return () => {}
}, [coliSN]);
const regularText = () => {
if (orderDetail.buytime > 0) return "(R" + orderDetail.buytime + ")"
return ''
}
const [openOrder, setOpenOrder] = useState(false)
return ( return (
<> <>
<Skeleton active loading={loading}>
<Flex gap='small' vertical={true} justify='space-between'> <Flex gap='small' vertical={true} justify='space-between'>
<Typography.Text> <Typography.Text>
<FieldNumberOutlined className='pr-1' /> <FieldNumberOutlined className='pr-1' />
LSS250501006 {orderDetail.order_no}
<HeartTwoTone twoToneColor='#eb2f96' />
<MoneyCollectTwoTone twoToneColor='#eb2f96' />
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text>
<UserOutlined className=' pr-1' /> <UserOutlined className=' pr-1' />
Jorgina(R1) {customerDetail.name + regularText()}
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text>
<CompassOutlined className=' pr-1' /> <CompassOutlined className=' pr-1' />
@ -48,34 +78,29 @@ const OrderProfile = ({coliSN, ...props}) => {
<Typography.Text> <Typography.Text>
<PhoneOutlined className=' pr-1' /> <PhoneOutlined className=' pr-1' />
<Button type='link' size={'small'} onClick={() => {}}> <Button type='link' size={'small'} onClick={() => {}}>
6596823833 {customerDetail.phone}
</Button> </Button>
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text>
<MailOutlined className=' pr-1' /> <MailOutlined className=' pr-1' />
Jorgina@gmail.com {customerDetail.email}
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text>
<WhatsAppOutlined className='pr-1' /> <WhatsAppOutlined className='pr-1' />
<Button type='link' size={'small'} onClick={() => {}}> <Button type='link' size={'small'} onClick={() => {}}>
6596826951 {customerDetail.whatsapp_phone_number}
</Button> </Button>
</Typography.Text> </Typography.Text>
<Typography.Text> <Typography.Text>
<CalendarOutlined className='pr-1' /> <CalendarOutlined className='pr-1' />
<span>出发日期</span>2025-09-18已下计划 <span>出发日期</span>2025-09-18已下计划
</Typography.Text> </Typography.Text>
<Typography.Text>
<span>特殊要求</span> </Flex>
在华城市 桂林 对酒店和房型要求 5-star <Divider orientation="left">
</Typography.Text>
<Typography.Text>
<span>外联备注</span>
泰国马来水灯节
</Typography.Text>
<Divider orientation="left">
<Typography.Text strong>订单状态</Typography.Text> <Typography.Text strong>订单状态</Typography.Text>
</Divider> </Divider>
<Flex gap='small' vertical={true} justify='space-between'>
<Select className={`[&_.ant-select-selection-item]:text-gray-950`} <Select className={`[&_.ant-select-selection-item]:text-gray-950`}
key={"orderlabel"} key={"orderlabel"}
size="small" size="small"
@ -85,10 +110,9 @@ const OrderProfile = ({coliSN, ...props}) => {
variant="underlined" variant="underlined"
onSelect={value => { onSelect={value => {
}} }}
value={0} value={orderDetail.tags}
options={orderLabelOptions} options={orderLabelOptions}
/> />
<Select className={`[&_.ant-select-selection-item]:text-gray-950`} <Select className={`[&_.ant-select-selection-item]:text-gray-950`}
key={"orderstatus"} key={"orderstatus"}
size="small" size="small"
@ -98,28 +122,37 @@ const OrderProfile = ({coliSN, ...props}) => {
variant="underlined" variant="underlined"
onSelect={value => { onSelect={value => {
}} }}
value={1} value={orderDetail.states}
options={orderStatusOptions} options={orderStatusOptions}
/> />
</Flex>
</Flex>
<Divider orientation="left"> <Divider orientation="left">
<Typography.Text strong>催信</Typography.Text> <Typography.Text strong>催信</Typography.Text>
</Divider> </Divider>
<Checkbox.Group key='substatus' className="px-2" options={remindStatusOptions} /> <Checkbox.Group key='substatus' className="px-2" options={remindStatusOptions} />
<Divider orientation='left'> <Divider orientation='left'>
<Typography.Text strong>表单信息</Typography.Text> <Typography.Text strong>表单信息</Typography.Text>
<Tooltip title='添加'> <Tooltip title='添加'>
<FileAddOutlined className='pl-1' /> <FileAddOutlined className='pl-1' />
</Tooltip> </Tooltip>
</Divider> </Divider>
<p <p className="p-2 overflow-auto m-0 break-words whitespace-pre-wrap" dangerouslySetInnerHTML={{ __html: orderDetail.order_detail }}></p>
className='p-2 overflow-auto m-0 break-words whitespace-pre-wrap'
dangerouslySetInnerHTML={{ <Divider orientation="left">
__html: <Typography.Text strong>特殊要求</Typography.Text>
'orderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detailorderDetail.order_detail', </Divider>
}}></p> <Typography.Text>
{orderDetail.customer_request}
</Typography.Text>
<Divider orientation="left">
<Typography.Text strong>外联备注</Typography.Text>
</Divider>
<Typography.Text>
{orderDetail.wl_memo}
</Typography.Text>
<Divider orientation='left'> <Divider orientation='left'>
<Typography.Text strong>附加信息</Typography.Text> <Typography.Text strong>附加信息</Typography.Text>
@ -129,7 +162,8 @@ const OrderProfile = ({coliSN, ...props}) => {
</Divider> </Divider>
<Typography.Text> <Typography.Text>
泰国马来水灯节 泰国马来水灯节
</Typography.Text> </Typography.Text>
</Skeleton>
</> </>
) )
} }

@ -65,14 +65,14 @@ export const useOrderStore = create(devtools((set, get) => ({
set(() => ({ set(() => ({
orderDetail: {...orderResult, coli_sn: colisn }, orderDetail: {...orderResult, coli_sn: colisn },
customerDetail: orderResult.contact.length > 0 ? orderResult.contact[0] : {}, customerDetail: orderResult.contact.length > 0 ? orderResult.contact[0] : {},
lastQuotation: orderResult.quotes.length > 0 ? orderResult.quotes[0] : {}, // lastQuotation: orderResult.quotes.length > 0 ? orderResult.quotes[0] : {},
quotationList: orderResult.quotes, // quotationList: orderResult.quotes,
})) }))
return { return {
orderDetail: {...orderResult, coli_sn: colisn }, orderDetail: {...orderResult, coli_sn: colisn },
customerDetail: orderResult.contact.length > 0 ? orderResult.contact[0] : {}, customerDetail: orderResult.contact.length > 0 ? orderResult.contact[0] : {},
lastQuotation: orderResult.quotes.length > 0 ? orderResult.quotes[0] : {}, // lastQuotation: orderResult.quotes.length > 0 ? orderResult.quotes[0] : {},
quotationList: orderResult.quotes, // quotationList: orderResult.quotes,
} }
} else { } else {
throw new Error(json?.errmsg + ': ' + json.errcode) throw new Error(json?.errmsg + ': ' + json.errcode)

@ -1,17 +1,16 @@
import { useCallback, useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { ReloadOutlined, ReadOutlined, CheckSquareOutlined, StarOutlined, RightOutlined, LeftOutlined, ExpandOutlined, AppstoreOutlined } from '@ant-design/icons' import { ReloadOutlined, ReadOutlined, CheckSquareOutlined, RightOutlined, LeftOutlined, ExpandOutlined } from '@ant-design/icons'
import { Flex, Button, Tooltip, List, Form, Row, Col, Drawer, Dropdown, Input, Checkbox, DatePicker, Switch, Breadcrumb } from 'antd' import { Flex, Button, Tooltip, List, Form, Row, Col, Dropdown, Input, Checkbox, DatePicker, Switch, Breadcrumb, Skeleton } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useEmailList } from '@/hooks/useEmail'; import { useEmailList } from '@/hooks/useEmail'
import { isEmpty } from '@/utils/commons'; import { isEmpty } from '@/utils/commons'
import { MailboxDirIcon } from './MailboxDirIcon' import { MailboxDirIcon } from './MailboxDirIcon'
import { endWith } from 'rxjs';
const { RangePicker } = DatePicker const { RangePicker } = DatePicker
const PAGE_SIZE = 50; // const PAGE_SIZE = 50 //
const MailBox = ({ mailboxDir, onMailItemClick, ...props}) => { const MailBox = ({ mailboxDir, onMailItemClick, ...props }) => {
const DATE_RANGE_PRESETS = [ const DATE_RANGE_PRESETS = [
{ {
label: '本周', label: '本周',
@ -42,75 +41,77 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props}) => {
const { mailList, loading, error, refresh } = useEmailList(mailboxDir); const { mailList, loading, error, refresh } = useEmailList(mailboxDir);
const [pagination, setPagination] = useState({ const [pagination, setPagination] = useState({
current: 1, current: 1,
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
total: 0, total: 0,
pagedList: [] pagedList: [],
}); })
useEffect(() => { useEffect(() => {
if (mailList) { if (mailList) {
const total = mailList.length; const total = mailList.length
const pageCount = Math.ceil(total / PAGE_SIZE); const pageCount = Math.ceil(total / PAGE_SIZE)
setPagination(prev => ({ setPagination((prev) => ({
...prev, ...prev,
total, total,
pageCount, pageCount,
current: 1, // current: 1, //
pagedList: getPagedData(mailList, 1) pagedList: getPagedData(mailList, 1),
})); }))
} }
}, [mailList]); }, [mailList])
const getPagedData = (data, currentPage) => { const getPagedData = (data, currentPage) => {
const startIndex = (currentPage - 1) * PAGE_SIZE; const startIndex = (currentPage - 1) * PAGE_SIZE
const endIndex = Math.min(startIndex + PAGE_SIZE, data.length); const endIndex = Math.min(startIndex + PAGE_SIZE, data.length)
return data.slice(startIndex, endIndex); return data.slice(startIndex, endIndex)
}; }
const prePage = () => { const prePage = () => {
if (pagination.current > 1) { if (pagination.current > 1) {
const newCurrent = pagination.current - 1; const newCurrent = pagination.current - 1
setPagination(prev => ({ setPagination((prev) => ({
...prev, ...prev,
current: newCurrent, current: newCurrent,
pagedList: getPagedData(mailList, newCurrent) pagedList: getPagedData(mailList, newCurrent),
})); }))
} }
}; }
const nextPage = () => { const nextPage = () => {
if (pagination.current < Math.ceil(pagination.total / PAGE_SIZE)) { if (pagination.current < Math.ceil(pagination.total / PAGE_SIZE)) {
const newCurrent = pagination.current + 1; const newCurrent = pagination.current + 1
setPagination(prev => ({ setPagination((prev) => ({
...prev, ...prev,
current: newCurrent, current: newCurrent,
pagedList: getPagedData(mailList, newCurrent) pagedList: getPagedData(mailList, newCurrent),
})); }))
} }
}; }
console.info('props.breadcrumb: ', props.breadcrumb)
console.info('props.mailboxDir: ', mailboxDir)
const mailItemRender = (item) => { const mailItemRender = (item) => {
const isOrderNode = mailboxDir.COLI_SN > 0 const isOrderNode = mailboxDir.COLI_SN > 0
const orderNumber = (isEmpty(item.MAI_COLI_ID) || isOrderNode) ? '' : (item.MAI_COLI_ID + ' - ') const orderNumber = isEmpty(item.MAI_COLI_ID) || isOrderNode ? '' : item.MAI_COLI_ID + ' - '
const countryName = isEmpty(item.CountryCN) ? '' : ('[' + (item.CountryCN === null ? 'USA' : item.CountryCN) + '] ') const countryName = isEmpty(item.CountryCN) ? '' : '[' + item.CountryCN + '] '
const mailStateClass = item.MOI_ReadState === 0 ? 'font-bold' : '' const mailStateClass = item.MOI_ReadState === 0 ? 'font-bold' : ''
return ( return (
<li className='flex border border-solid border-t-0 border-x-0 border-gray-200 hover:bg-neutral-50 p-2' onClick={() => { <li
console.info('item: ', item) className='flex border border-solid border-t-0 border-x-0 border-gray-200 hover:bg-neutral-50 p-2'>
onMailItemClick(item)
}}>
<div className=''> <div className=''>
<Checkbox></Checkbox> <Checkbox></Checkbox>
</div> </div>
<div className='flex-1 pl-2'> <div className='flex-1 pl-2'
onClick={() => {
console.info('item: ', item)
onMailItemClick(item)
}}>
<Flex gap='small' vertical={true} justify='space-between' className='cursor-pointer'> <Flex gap='small' vertical={true} justify='space-between' className='cursor-pointer'>
<div>{orderNumber}<span className={mailStateClass}>{item.MAI_Subject}</span></div> <div>
{orderNumber}
<span className={mailStateClass}>{item.MAI_Subject}</span>
</div>
<span className='text-neutral-500 text-wrap break-words break-all '>{countryName + item.SenderReceiver + ' ' + item.SRDate}</span> <span className='text-neutral-500 text-wrap break-words break-all '>{countryName + item.SenderReceiver + ' ' + item.SRDate}</span>
</Flex> </Flex>
</div> </div>
@ -187,10 +188,9 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props}) => {
}} }}
placeholder={`邮件主题`} placeholder={`邮件主题`}
/> />
<Tooltip title='高级搜索'> <Tooltip title='高级搜索'>
<Switch checkedChildren={<ExpandOutlined />} <Switch checkedChildren={<ExpandOutlined />} unCheckedChildren={<ExpandOutlined />} defaultChecked={false} />
unCheckedChildren={<ExpandOutlined />} defaultChecked={false} /> </Tooltip>
</Tooltip>
</div> </div>
<div className='bg-white h-auto p-1 flex gap-1 items-center hidden'> <div className='bg-white h-auto p-1 flex gap-1 items-center hidden'>
<Form <Form
@ -219,41 +219,50 @@ const MailBox = ({ mailboxDir, onMailItemClick, ...props}) => {
</div> </div>
<div className='bg-white overflow-y-auto' style={{ height: 'calc(100vh - 198px)' }}> <div className='bg-white overflow-y-auto' style={{ height: 'calc(100vh - 198px)' }}>
<List loading={loading} <Skeleton active loading={loading}>
<List
loading={loading}
header={ header={
<Flex align='center' justify="space-between"> <Flex align='center' justify='space-between'>
<Breadcrumb <Breadcrumb
items={ items={props.breadcrumb.map((bc) => {
props.breadcrumb.map(bc => {
return { return {
title: ( title: (
<> <>
<MailboxDirIcon type={bc?.iconIndex} /> <MailboxDirIcon type={bc?.iconIndex} />
<span>{bc.title}</span> <span>{bc.title}</span>
</> </>
), ),
} }
})} })}
/> />
<Flex align="center" justify="space-between"> <Flex align='center' justify='space-between'>
<span> <span>
{((pagination.current - 1) * PAGE_SIZE + 1)}-{Math.min(pagination.current * PAGE_SIZE, pagination.total)} of {pagination.total} {(pagination.current - 1) * PAGE_SIZE + 1}-{Math.min(pagination.current * PAGE_SIZE, pagination.total)} of {pagination.total}
</span> </span>
<Button icon={<LeftOutlined />} type="text" onClick={() => { <Button
prePage() icon={<LeftOutlined />}
}} iconPosition={'end'}></Button> type='text'
<Button icon={<RightOutlined />} type="text" onClick={() => { onClick={() => {
nextPage() prePage()
}} iconPosition={'end'}></Button> }}
iconPosition={'end'}></Button>
<Button
icon={<RightOutlined />}
type='text'
onClick={() => {
nextPage()
}}
iconPosition={'end'}></Button>
</Flex>
</Flex> </Flex>
</Flex>
} }
itemLayout='vertical' itemLayout='vertical'
pagination={false} pagination={false}
dataSource={pagination.pagedList} dataSource={pagination.pagedList}
renderItem={mailItemRender} renderItem={mailItemRender}
/> />
</Skeleton>
</div> </div>
</> </>
) )

Loading…
Cancel
Save