You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Global-sales/src/views/orders/Follow.jsx

416 lines
13 KiB
React

import { Conditional } from '@/components/Conditional'
import useAuthStore from '@/stores/AuthStore'
import useFormStore from '@/stores/FormStore'
import { useOrderStore, OrderLabelDefaultOptions, OrderStatusDefaultOptions, RemindStateDefaultOptions } from '@/stores/OrderStore'
import { copy, isNotEmpty, isEmpty } from '@/utils/commons'
import { InfoCircleTwoTone, MailTwoTone, MessageTwoTone, PhoneTwoTone, WhatsAppOutlined } from '@ant-design/icons'
import {
App, Badge, Button,
Col,
DatePicker,
Empty,
Flex, Form, Input,
Radio, Row,
Select, Space, Switch, Table,
Tabs,
Tag, Tooltip
} from 'antd'
import dayjs from 'dayjs'
import { memo, useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { useShallow } from 'zustand/react/shallow'
const { RangePicker } = DatePicker
const AdvanceSearchForm = memo(function noName({ initialValues, onSubmit }) {
const DATE_RANGE_PRESETS = [
{
label: '本周',
value: [dayjs().startOf('w'), dayjs().endOf('w')],
},
{
label: '上周',
value: [dayjs().startOf('w').subtract(7, 'days'), dayjs().endOf('w').subtract(7, 'days')],
},
{
label: '本月',
value: [dayjs().startOf('M'), dayjs().endOf('M')],
},
{
label: '上月',
value: [dayjs().subtract(1, 'M').startOf('M'), dayjs().subtract(1, 'M').endOf('M')],
},
{
label: '前三月',
value: [dayjs().subtract(2, 'M').startOf('M'), dayjs().endOf('M')],
},
{
label: '本年',
value: [dayjs().startOf('y'), dayjs().endOf('y')],
},
]
const orderLabelOptions = copy(OrderLabelDefaultOptions)
orderLabelOptions.unshift({ value: '', label: '全部' })
const orderStatusOptions = copy(OrderStatusDefaultOptions)
orderStatusOptions.unshift({ value: '', label: '全部' })
const remindStateOptions = copy(RemindStateDefaultOptions)
remindStateOptions.unshift({ value: '', label: '全部' })
const [form] = Form.useForm()
function handleSubmit(values) {
onSubmit?.(values)
}
return (
<Form
layout={'vertical'}
form={form}
initialValues={{
orderLabel: '', orderStatus: '', remindState: '', ...initialValues
}}
onFinish={handleSubmit}
>
<Row justify='start' gutter={16}>
<Col span={4}>
<Form.Item label='订单号' name='orderNumber'>
<Input placeholder='订单号' allowClear />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='邮件地址/客人电话' name='emailOrPhone'>
<Input placeholder='邮件地址/客人电话' allowClear />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='First name' name='firstName'>
<Input placeholder='First name' allowClear />
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='Last name' name='lastName'>
<Input placeholder='Last name' allowClear />
</Form.Item>
</Col>
</Row>
<Row justify='start' align='middle' gutter={16}>
<Col span={2}>
<Form.Item label='标签' name='orderLabel'>
<Select
options={orderLabelOptions}
/>
</Form.Item>
</Col>
<Col span={2}>
<Form.Item label='状态' name='orderStatus'>
<Select
options={orderStatusOptions}
/>
</Form.Item>
</Col>
<Col span={2}>
<Form.Item label='催信' name='remindState'>
<Select
options={remindStateOptions}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='出发日期' name='startDateRange'>
<RangePicker
allowClear={true}
inputReadOnly={true}
presets={DATE_RANGE_PRESETS}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label='确认日期' name='confirmDateRange'>
<RangePicker
allowClear={true}
inputReadOnly={true}
presets={DATE_RANGE_PRESETS}
/>
</Form.Item>
</Col>
<Col span={1} offset={1}>
<Button type='primary' htmlType='submit'>搜索</Button>
</Col>
</Row>
</Form>
)
})
function OrderGroupTable({ formValues }) {
const orderColumns = [
{
title: '订单号',
dataIndex: 'COLI_ID',
width: 222,
render: (text, record) => {
let tagIcon = ''
if (record.COLI_LineGrade === 240003) tagIcon = <Tag color='red'>重点</Tag>
else if (record.COLI_LineGrade === 240002) tagIcon = <Tag color='green'>次重点</Tag>
else if (record.COLI_LineGrade === 240001) tagIcon = <Tag color='blue'>一般</Tag>
return (
<Space>
<Link to={`/order/chat/${record.COLI_SN}`} state={record}>
{text}
</Link>
{tagIcon}
</Space>
)
}
},
{
title: '客人姓名',
dataIndex: 'coli_guest',
render: (text, record) => {
let regularText = ''
if (record.buytime > 0) regularText = '(R' + record.buytime + ')'
return (
<Space>
<Conditional
condition={isNotEmpty(record.coli_guest_WhatsApp)}
whenTrue={<Tooltip title={record.coli_guest_WhatsApp}><WhatsAppOutlined className={['pl-1', record.last_received_time ? 'text-whatsapp' : 'text-neutral-500']} /></Tooltip>}
/>
{text + regularText}
<Badge
count={record.unread_msg}
style={{
backgroundColor: '#52c41a',
}}
/>
</Space>
)
}
},
{
title: '订单状态',
dataIndex: 'COLI_State',
width: 120,
render: (text, record) => {
// 1新订单<InfoCircleTwoTone />2未读消息<MessageTwoTone />3需一催4需二催5需三催<PhoneTwoTone />6未处理邮件<MailTwoTone />
const needTo = '要催信' + (record.coli_ordertype - 2)
let statusIcon = ''
if (record.coli_ordertype === 1) statusIcon = <Tooltip title='新订单'><InfoCircleTwoTone /></Tooltip>
else if (record.coli_ordertype === 2) statusIcon = <Tooltip title='新消息'><MessageTwoTone /></Tooltip>
else if (record.coli_ordertype === 3 || record.coli_ordertype === 4 || record.coli_ordertype === 5) statusIcon = <Tooltip title={needTo}><PhoneTwoTone /></Tooltip>
else if (record.coli_ordertype === 6) statusIcon = <Tooltip title='老邮件'><MailTwoTone /></Tooltip>
return (
<Space>
{statusIcon}
{text}
</Space>
)
}
},
{
title: '报价 Title',
dataIndex: 'lettertitle',
ellipsis: true,
hidden: false,
// render: (text, record) => {
// return (
// <Tooltip title={text}>{text}</Tooltip>
// )
// }
},
{
title: '出发日期',
dataIndex: 'COLI_OrderStartDate',
width: 120,
hidden: false,
sortDirections: ['ascend', 'descend'],
sorter: (a, b) => {
const datejsA = isEmpty(a.COLI_OrderStartDate) ? 0 : new dayjs(a.COLI_OrderStartDate).valueOf()
const datejsB = isEmpty(b.COLI_OrderStartDate) ? 0 : new dayjs(b.COLI_OrderStartDate).valueOf()
return datejsA - datejsB
}
},
{
title: '客人最后一次回复时间',
dataIndex: 'last_received_time',
width: 180,
render: (text, record) => {
if (record.last_received_time) {
return new dayjs(record.last_received_time).format('YYYY-MM-DD HH:mm:ss')
}
}
},
{
title: '附加信息',
ellipsis: true,
dataIndex: 'COLI_Introduction',
},
]
const { notification } = App.useApp()
const [loading, setLoading] = useState(false)
const orderList = useOrderStore((state) => state.orderList)
const fetchOrderList = useOrderStore((state) => state.fetchOrderList)
const loginUser = useAuthStore((state) => state.loginUser)
useEffect(() => {
let canSearch = true
if (formValues.type === 'advance') {
const copyObject = copy(formValues)
delete copyObject.type
const allEmpty = Object.values(copyObject).every(val => {
return val === null || val === '' || val === undefined
})
if (allEmpty) {
canSearch = false
notification.warning({
message: '温馨提示',
description: '请输入至少一个条件',
placement: 'top',
duration: 60,
})
}
}
if (canSearch) {
setLoading(true)
fetchOrderList(formValues, loginUser)
.finally(() => setLoading(false))
.catch(reason => {
notification.error({
message: '查询出错',
description: reason.message,
placement: 'top',
duration: 60,
})
})
}
}, [formValues])
const paginationProps = {
showQuickJumper: true,
showLessItems: true,
showSizeChanger: true,
showTotal: (total) => { return `总数:${total}` }
}
function groupByParam(array, param) {
return array.reduce((result, item) => {
(result[item[param]] = result[item[param]] || []).push(item)
return result
}, {})
}
const deptMap = new Map([
['1', 'CH直销组'],
['2', 'CH大客户组'],
['7', '市场推广'],
['8', '德语市场'],
['9', '日语市场'],
['10', '商旅市场'],
['11', '法语市场'],
['12', '西语市场'],
['13', '英文在线组'],
['14', '商务Biztravel'],
['15', 'CH产品'],
['16', 'APP移动项目组'],
['17', 'ChinaTravel组'],
['18', 'CT市场'],
['20', '俄语市场'],
['21', '意语市场'],
['22', '爱游网'],
['23', '三峡站'],
['24', '桂林站'],
['25', '上海站'],
['26', '北京站'],
['27', '西藏站'],
['28', 'AH亚洲项目组'],
['29', 'DMC地接组'],
['30', 'Trippest项目组'],
['31', '花梨鹰'],
['32', 'Daytours板块'],
['33', 'GH项目组'],
['34', 'trippest网站'],
['35', 'newsletter营销'],
])
const groupOrderData = groupByParam(orderList, 'OPI_DEI_SN')
const deptKeys = Object.keys(groupOrderData)
const deptItems = []
deptKeys.forEach((deptNo, index) => {
const deptOrderList = groupOrderData[deptNo]
deptItems.push(
{
key: index,
label: deptMap.get(deptNo),
children: <Table key={'Order Table' + deptNo} loading={loading} dataSource={deptOrderList}
columns={orderColumns}
pagination={deptOrderList.length <= 10 ? false : paginationProps} />
}
)
})
return (
<Conditional
condition={orderList.length > 0}
whenTrue={<Tabs defaultActiveKey={0} items={deptItems} />}
whenFalse={<Empty />}
/>
)
}
function Follow() {
const [formValues, setFormValues] = useFormStore(useShallow((state) => [state.orderFollowForm, state.setOrderFollowForm]))
const [advanceChecked, toggleAdvance] = useFormStore(useShallow((state) => [state.orderFollowAdvanceChecked, state.setOrderFollowAdvanceChecked]))
const handleSubmit = useCallback((values) => {
setFormValues({ ...values, type: 'advance' })
}, [])
return (
<>
<Space direction='vertical' size='large' style={{ width: '100%' }}>
<Flex gap='large' justify='start' align='center' horizontal='true'>
<Radio.Group
options={[
{ label: '今日任务', value: 'today' },
{ label: '重点订单', value: 'zhongdian' },
{ label: '次重点客户', value: 'qianli' },
{ label: '成行', value: 'chengxing' },
{ label: '走团中', value: 'zoutuan' },
{ label: '走团后一月', value: 'zoutuanhou' }
]}
value={formValues.type}
onChange={({ target: { value } }) => {
setFormValues({
...formValues,
type: value
})
}}
optionType='button'
buttonStyle='solid'
disabled={advanceChecked}
/>
<Switch checkedChildren='高级查询' unCheckedChildren='高级查询'
checked={advanceChecked}
onChange={() => { toggleAdvance(!advanceChecked) }} />
</Flex>
<Conditional condition={advanceChecked} whenTrue={<AdvanceSearchForm onSubmit={handleSubmit} initialValues={formValues} />}
/>
<OrderGroupTable formValues={formValues} />
</Space>
</>
)
}
export default Follow