todo: 会话右键菜单; 标签筛选; 订单标记筛选; 会话列表显示
parent
f54c1e790a
commit
305502920d
@ -0,0 +1,29 @@
|
|||||||
|
import Icon from '@ant-design/icons';
|
||||||
|
const WABSvg = () => (
|
||||||
|
<svg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg' width='16' height='16'>
|
||||||
|
<path
|
||||||
|
d='M16.065 29.045h-.005a13.27 13.27 0 01-6.74-1.836l-.484-.287-5.012 1.31 1.338-4.865-.315-.498a13.102 13.102 0 01-2.025-7.014C2.825 8.588 8.766 2.676 16.071 2.676a13.185 13.185 0 019.362 3.866 13.068 13.068 0 013.875 9.324c-.003 7.267-5.943 13.18-13.243 13.18zM27.336 4.65A15.868 15.868 0 0016.066 0C7.281-.002.135 7.111.131 15.853a15.771 15.771 0 002.127 7.927l-2.26 8.217 8.446-2.205a15.982 15.982 0 007.614 1.93h.006c8.781 0 15.93-7.114 15.933-15.856a15.724 15.724 0 00-4.663-11.219z'
|
||||||
|
fill='#2ba84a'
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d='M10.273 23.549c-.18-.105-.356-.197-.356-.769.004-2.836.009-9.394 0-11.82-.005-1.527-.209-2.515 1.14-2.515 3.65 0 8.983-.677 10.225 2.31 1.253 3.02-.774 4.483-1.219 5.26 3.042.842 3.208 7.593-3.293 7.593-1.391 0-3.348.005-5.615.01-.533 0-.77 0-.882-.07zm2.816-2.475h3.301c1.406-.004 2.657-.649 2.625-2.031-.023-1.3-.9-1.729-2.12-1.848-1.154.014-2.48.014-3.806.014v3.865zm0-6.476c2.443-.033 3.385.095 4.72-.234.918-.512 1.317-2.42.005-3.064-.909-.45-3.608-.297-4.725-.252v3.55z'
|
||||||
|
fill='#2ba84a'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
export const WABIcon = (props) => <Icon component={WABSvg} {...props} />;
|
||||||
|
|
||||||
|
const Read = () => (
|
||||||
|
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 24 24" color="#4fc3f7" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M0 0h24v24H0z" stroke="none"/><path d="M18 7l-1.41-1.41-6.34 6.34 1.41 1.41L18 7zm4.24-1.41L11.66 16.17 7.48 12l-1.41 1.41L11.66 19l12-12-1.42-1.41zM.41 13.41L6 19l1.41-1.41L1.83 12 .41 13.41z" stroke="none"/></svg>
|
||||||
|
)
|
||||||
|
export const ReadIcon = (props) => <Icon component={Read} {...props} />;
|
||||||
|
|
||||||
|
const Deliver = () => (
|
||||||
|
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M317.5 210.3c1.7-1.8 1.8-4.7 0-6.5l-19.8-21c-.8-.9-2-1.4-3.2-1.4-1.2 0-2.4.5-3.2 1.4l-66.5 69.1 26.4 27.1 66.3-68.7zm-193.7 42.8c-.9-.9-2-1.4-3.2-1.4-1.2 0-2.3.5-3.2 1.4l-20.1 20.7c-1.8 1.8-1.8 4.8 0 6.6l63.2 65c4 4.2 9 6.6 13.2 6.6 6 0 11.1-4.5 13.1-6.4l.1-.1 13.4-13.8-76.5-78.6z" stroke="none"/><path d="M414.7 182.4l-19.8-21c-.8-.9-2-1.4-3.2-1.4-1.2 0-2.4.5-3.2 1.4L250.7 304.1l-50.1-51.6c-.9-.9-2-1.4-3.2-1.4-1.2 0-2.3.5-3.2 1.4l-20.1 20.7c-1.8 1.8-1.8 4.8 0 6.6l63.2 65c4 4.2 9 6.6 13.2 6.6 6 0 11.1-4.5 13.1-6.4l.1-.1 151-156.1c1.7-1.7 1.7-4.6 0-6.4z" stroke="none"/></svg>
|
||||||
|
)
|
||||||
|
export const DeliverIcon = (props) => <Icon component={Deliver} {...props} />;
|
||||||
|
|
||||||
|
const Sent = () => (
|
||||||
|
<svg stroke="currentColor" fill="currentColor" strokeWidth="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path fill="none" d="M0 0h24v24H0z" stroke="none"/><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" stroke="none"/></svg>
|
||||||
|
)
|
||||||
|
export const SentIcon = (props) => <Icon component={Sent} {...props} />;
|
@ -0,0 +1,17 @@
|
|||||||
|
import React, { } from 'react';
|
||||||
|
import { WhatsAppOutlined, MailOutlined } from '@ant-design/icons';
|
||||||
|
import { WABIcon, } from '@/components/Icons';
|
||||||
|
|
||||||
|
const ChannelLogo = ({channel}) => {
|
||||||
|
switch (channel) {
|
||||||
|
case 'waba':
|
||||||
|
return <WABIcon key={channel} className='text-whatsapp' />;
|
||||||
|
case 'wa':
|
||||||
|
return <WhatsAppOutlined key={channel} className='text-whatsapp' />;
|
||||||
|
case 'email':
|
||||||
|
return <MailOutlined key={channel} className='text-violet-500' />
|
||||||
|
default:
|
||||||
|
return <MailOutlined key={'channel'} className='text-violet-500' />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default ChannelLogo;
|
@ -0,0 +1,105 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button, Tag, Radio, Popover, Form } from 'antd';
|
||||||
|
import { FilterOutlined, FilterTwoTone } from '@ant-design/icons';
|
||||||
|
import { isEmpty, objectMapper, stringToColour } from '@/utils/commons';
|
||||||
|
import useConversationStore from '@/stores/ConversationStore';
|
||||||
|
|
||||||
|
const otypes = [
|
||||||
|
{ label: 'All', value: '' },
|
||||||
|
{ label: '重点', value: 'zhongdian' },
|
||||||
|
{ label: '次重点', value: 'qianli' },
|
||||||
|
{ label: '成行', value: 'chengxing' },
|
||||||
|
{ label: '走团中', value: 'zoutuan' },
|
||||||
|
];
|
||||||
|
const otypesMapped = otypes.reduce((acc, cur) => ({ ...acc, [cur.value]: cur }), {});
|
||||||
|
const TagColorStyle = (tag, outerStyle = false) => {
|
||||||
|
const color = stringToColour(tag);
|
||||||
|
const outerStyleObj = outerStyle ? { borderColor: `${color}66`, backgroundColor: `${color}0D` } : {};
|
||||||
|
return { color: `${color}`, ...outerStyleObj };
|
||||||
|
};
|
||||||
|
const ChatListFilter = ({ ...props }) => {
|
||||||
|
const handleFilter = async (param) => {};
|
||||||
|
|
||||||
|
const [
|
||||||
|
{ tags: selectedTags, otype: selectedOType, ...filter },
|
||||||
|
setFilterTags, setFilterOtype, resetFilter
|
||||||
|
] = useConversationStore((state) => [
|
||||||
|
state.filter,
|
||||||
|
state.setFilterTags, state.setFilterOtype, state.resetFilter
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [tags] = useConversationStore((state) => [state.tags]);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const handleTagsChange = (tag, checked) => {
|
||||||
|
const nextSelectedTags = checked ? [...selectedTags, tag.key] : selectedTags.filter((t) => t !== tag.key);
|
||||||
|
setFilterTags(nextSelectedTags);
|
||||||
|
form.setFieldValue('tags', nextSelectedTags);
|
||||||
|
};
|
||||||
|
const onFinish = async (values) => {
|
||||||
|
const filterParam = objectMapper(values, { tags: {key:'tags', transform: (v) => v.join(',')} });
|
||||||
|
console.log('Received values of form[filter_form]: ', values, ' \n', filterParam);
|
||||||
|
await handleFilter(filterParam);
|
||||||
|
setOpenPopup(false);
|
||||||
|
};
|
||||||
|
const onReset = () => {
|
||||||
|
resetFilter();
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
const [openPopup, setOpenPopup] = useState(false);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className='my-1 flex justify-between items-center '>
|
||||||
|
<Radio.Group optionType={'button'} buttonStyle='solid' size='small' options={otypes} value={selectedOType} onChange={(e) => setFilterOtype(e.target.value)} />
|
||||||
|
<Popover destroyTooltipOnHide
|
||||||
|
placement='bottom' overlayClassName='max-w-80'
|
||||||
|
trigger={'click'}
|
||||||
|
open={openPopup}
|
||||||
|
onOpenChange={setOpenPopup}
|
||||||
|
title={
|
||||||
|
<div className='flex justify-between '>
|
||||||
|
<div>更多筛选</div>
|
||||||
|
<Button size='small' onClick={() => setOpenPopup(false)}>
|
||||||
|
×
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<Form form={form} name='conversation_filter_form' layout='vertical' size='small' initialValues={{}} onFinish={onFinish} className='*:mb-2'>
|
||||||
|
<Form.Item name={'otype'} label='订单'>
|
||||||
|
<Tag key={selectedOType} closeIcon={selectedOType!==''} onClose={() => setFilterOtype('')}>
|
||||||
|
{otypesMapped[selectedOType].label}
|
||||||
|
</Tag>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name={'tags'} label='标签' className='*.div:gap-1'>
|
||||||
|
{tags.map((tag, ti) => (
|
||||||
|
<Tag.CheckableTag className='mb-1'
|
||||||
|
key={tag.key}
|
||||||
|
checked={selectedTags.includes(tag.key)}
|
||||||
|
onChange={(checked) => handleTagsChange(tag, checked)}
|
||||||
|
style={TagColorStyle(tag.label, selectedTags.includes(tag.key))}>
|
||||||
|
{tag.label}
|
||||||
|
</Tag.CheckableTag>
|
||||||
|
))}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item noStyle className='flex justify-center mb-0'>
|
||||||
|
<Button.Group>
|
||||||
|
<Button onClick={onReset} type='primary' ghost>
|
||||||
|
重置
|
||||||
|
</Button>
|
||||||
|
<Button htmlType='submit' type='primary'>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</Button.Group>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
}>
|
||||||
|
<Button icon={isEmpty(selectedTags) ? <FilterOutlined /> : <FilterTwoTone />} type='text' size='middle' />
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ChatListFilter;
|
@ -0,0 +1,316 @@
|
|||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||||
|
import { Dropdown, Input, Button, Empty, Tooltip, Tag, Select, Divider, Radio, Popover, theme, Form } from 'antd';
|
||||||
|
import { PlusOutlined, WhatsAppOutlined, LoadingOutlined, HistoryOutlined, FireOutlined,AudioTwoTone, FilterOutlined, TagsOutlined, TagsTwoTone, FilterTwoTone, MailOutlined, CloseOutlined, CloseCircleOutlined } from '@ant-design/icons';
|
||||||
|
import { fetchConversationsList, fetchOrderConversationsList, fetchConversationItemClose, fetchConversationsSearch, postNewConversationItem, fetchConversationItemUnread, fetchConversationItemTop, UNREAD_MARK, postConversationTags, deleteConversationTags } from '@/actions/ConversationActions';
|
||||||
|
import { ChatItem } from 'react-chat-elements';
|
||||||
|
// import ConversationsNewItem from './ConversationsNewItem';
|
||||||
|
import { isEmpty, olog, stringToColour } from '@/utils/commons';
|
||||||
|
import useConversationStore from '@/stores/ConversationStore';
|
||||||
|
import useAuthStore from '@/stores/AuthStore';
|
||||||
|
import { useVisibilityState } from '@/hooks/useVisibilityState';
|
||||||
|
import { OrderLabelDefaultOptions, OrderStatusDefaultOptions, RemindStateDefaultOptions } from '@/stores/OrderStore'
|
||||||
|
import ChannelLogo from './ChannelLogo';
|
||||||
|
import { DeliverIcon, ReadIcon, SentIcon, WABIcon } from '@/components/Icons';
|
||||||
|
const { Option, OptGroup } = Select;
|
||||||
|
const { useToken } = theme;
|
||||||
|
|
||||||
|
const TagColorStyle = (tag) => {
|
||||||
|
const color = stringToColour(tag);
|
||||||
|
return { color: `${color}`, borderColor: `${color}66`, backgroundColor: `${color}0D` }
|
||||||
|
}
|
||||||
|
const TagColorStyle_2 = (tag, outerStyle = false) => {
|
||||||
|
const color = stringToColour(tag);
|
||||||
|
const outerStyleObj = outerStyle ? { borderColor: `${color}66`, } : {};
|
||||||
|
return { color: `${color}`, ...outerStyleObj };
|
||||||
|
};
|
||||||
|
|
||||||
|
const NewTagForm = ({onSubmit,...props}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [subLoding, setSubLoding] = useState(false);
|
||||||
|
const [tags, addTag] = useConversationStore(state => [state.tags, state.addTag]);
|
||||||
|
const onFinish = async (values) => {
|
||||||
|
console.log('Received values of form[new_tag]: ', values);
|
||||||
|
setSubLoding(true);
|
||||||
|
if (typeof onSubmit === 'function') {
|
||||||
|
onSubmit();
|
||||||
|
}
|
||||||
|
// debug:
|
||||||
|
setTimeout(() => {
|
||||||
|
setSubLoding(false);
|
||||||
|
addTag({ label: values.tag_label, key: values.tag_label, value: values.tag_label })
|
||||||
|
}, 2000);
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
name='new_tag_form'
|
||||||
|
layout='inline' size='small'
|
||||||
|
initialValues={{}}
|
||||||
|
onFinish={onFinish}>
|
||||||
|
<Form.Item name={'tag_label'} rules={[{ required: true, message: '请输入标签名' }]}>
|
||||||
|
<Input placeholder='新增并设置' />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Button type='primary' htmlType='submit' loading={subLoding} >
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const EditChatMemoForm = ({onSubmit,...props}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [subLoding, setSubLoding] = useState(false);
|
||||||
|
const onFinish = async (values) => {
|
||||||
|
console.log('Received values of form[chat_memo]: ', values);
|
||||||
|
setSubLoding(true);
|
||||||
|
// debug:
|
||||||
|
setTimeout(() => {
|
||||||
|
setSubLoding(false);
|
||||||
|
}, 2000);
|
||||||
|
if (typeof onSubmit === 'function') {
|
||||||
|
onSubmit();
|
||||||
|
}
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
name='chat_memo_form'
|
||||||
|
layout='inline' size='small'
|
||||||
|
initialValues={{}}
|
||||||
|
onFinish={onFinish}>
|
||||||
|
<Form.Item name={'memo'} rules={[{ required: true, message: '请输入备注' }]}>
|
||||||
|
<Input placeholder='输入备注' width={400} className='w-64' />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Button type='primary' htmlType='submit' loading={subLoding} >
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,onSwitchConversation,tabSelectedConversation, ...props}) => {
|
||||||
|
const routerReplace = mobile === undefined ? true : false; // : true;
|
||||||
|
const routePrefix = mobile === undefined ? `/order/chat` : `/m/chat`;
|
||||||
|
const { state: orderRow } = useLocation();
|
||||||
|
const { coli_guest_WhatsApp } = orderRow || {};
|
||||||
|
const { order_sn } = useParams();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const userId = useAuthStore((state) => state.loginUser.userId);
|
||||||
|
const initialState = useConversationStore((state) => state.initialState);
|
||||||
|
const [currentConversation, setCurrentConversation] = useConversationStore((state) => [state.currentConversation, state.setCurrentConversation]);
|
||||||
|
const conversationsList = useConversationStore((state) => state.conversationsList);
|
||||||
|
const [conversationsListLoading, setConversationsListLoading] = useConversationStore((state) => [state.conversationsListLoading, state.setConversationsListLoading]);
|
||||||
|
const addToConversationList = useConversationStore((state) => state.addToConversationList);
|
||||||
|
const delConversationitem = useConversationStore((state) => state.delConversationitem);
|
||||||
|
|
||||||
|
const closedConversationsList = useConversationStore((state) => state.closedConversationsList);
|
||||||
|
const setClosedConversationList = useConversationStore((state) => state.setClosedConversationList);
|
||||||
|
|
||||||
|
const itemTagsKeys = (item.tags || []).map(t => t.key);
|
||||||
|
const [tags, addTag] = useConversationStore(state => [state.tags, state.addTag]);
|
||||||
|
const handleConversationItemClose = async (item) => {
|
||||||
|
await fetchConversationItemClose({ conversationid: item.sn, opisn: item.opi_sn });
|
||||||
|
delConversationitem(item);
|
||||||
|
if (String(order_sn) === String(item.coli_sn)) {
|
||||||
|
navigate(routePrefix, { replace: routerReplace });
|
||||||
|
}
|
||||||
|
const _clist = await fetchConversationsSearch({ opisn: userId, session_enable: 0 });
|
||||||
|
setClosedConversationList(_clist);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConversationItemUnread = async (item) => {
|
||||||
|
await fetchConversationItemUnread({ conversationid: item.sn });
|
||||||
|
await refreshConversationList();
|
||||||
|
setListUpdateFlag(Math.random());
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConversationItemTop = async (item) => {
|
||||||
|
await fetchConversationItemTop({ conversationid: item.sn, top_state: item.top_state === 0 ? 1 : 0 });
|
||||||
|
await refreshConversationList();
|
||||||
|
setListUpdateFlag(Math.random());
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConversationItemTags = async (item, tagKey) => {
|
||||||
|
const _tags = item.tags || [];
|
||||||
|
if (_tags.includes(tagKey)) {
|
||||||
|
await deleteConversationTags({ conversationid: item.sn, tag_id: tagKey, opisn: userId })
|
||||||
|
} else {
|
||||||
|
await postConversationTags({ conversationid: item.sn, tag_id: tagKey, opisn: userId });
|
||||||
|
}
|
||||||
|
await refreshConversationList();
|
||||||
|
setListUpdateFlag(Math.random());
|
||||||
|
}
|
||||||
|
|
||||||
|
const [contextMenuOpen, setContextMenuOpen] = useState(false);
|
||||||
|
const handleContextMenuOpenChange = (nextOpen, info) => {
|
||||||
|
if (info.source === 'trigger' || nextOpen) {
|
||||||
|
setContextMenuOpen(nextOpen);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const [openTags, setOpenTags] = useState([]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (contextMenuOpen === false) {
|
||||||
|
setOpenTags([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {};
|
||||||
|
}, [contextMenuOpen])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dropdown
|
||||||
|
key={item.sn}
|
||||||
|
destroyPopupOnHide
|
||||||
|
trigger={['contextMenu']}
|
||||||
|
open={contextMenuOpen}
|
||||||
|
onOpenChange={handleContextMenuOpenChange}
|
||||||
|
menu={{
|
||||||
|
items: [
|
||||||
|
{ label: '置顶会话', key: 'top' },
|
||||||
|
// { label: '取消置顶', key: 'no_top' },
|
||||||
|
{ label: '标记为未读', key: 'unread' },
|
||||||
|
{
|
||||||
|
label: '设置标签',
|
||||||
|
key: 'tags',
|
||||||
|
children: [
|
||||||
|
...tags.map((t) => ({
|
||||||
|
...t,
|
||||||
|
key: `tag_${t.key}`,
|
||||||
|
style: { color: stringToColour(t.label) },
|
||||||
|
icon: itemTagsKeys.includes(t.key) ? <CloseCircleOutlined /> : false,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
<Popover content={<NewTagForm onSubmit={() => setContextMenuOpen(false)} />} placement='bottom' trigger={['click']}>
|
||||||
|
{/* todo: refresh list */}
|
||||||
|
<Button type='dashed' size='small' className='m-1'>
|
||||||
|
+新标签
|
||||||
|
</Button>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
key: 'new_tags',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onTitleClick: ({ key, domEvent }) => {
|
||||||
|
console.log(']]]', key);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
{/* todo: refresh list */}
|
||||||
|
<Popover overlayClassNam1e='w-80' content={<EditChatMemoForm onSubmit={() => setContextMenuOpen(false)} />} placement='bottom' trigger={['click']}>
|
||||||
|
{/* <Button type='text' size='small' className='m-1'> */}
|
||||||
|
编辑联系人
|
||||||
|
{/* </Button> */}
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
key: 'remark',
|
||||||
|
},
|
||||||
|
{ type: 'divider' },
|
||||||
|
{ label: '隐藏会话', key: 'close', danger: true },
|
||||||
|
],
|
||||||
|
triggerSubMenuAction: 'click',
|
||||||
|
openKeys: openTags,
|
||||||
|
onOpenChange: (openKeys) => {
|
||||||
|
if (!isEmpty(openKeys) && contextMenuOpen) {
|
||||||
|
setOpenTags(openKeys);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: ({ key, domEvent }) => {
|
||||||
|
domEvent.stopPropagation();
|
||||||
|
if (key.startsWith('tag_')) {
|
||||||
|
const tagKey = key.replace('tag_', '');
|
||||||
|
return handleConversationItemTags(item, tagKey);
|
||||||
|
}
|
||||||
|
switch (key) {
|
||||||
|
case 'top':
|
||||||
|
setContextMenuOpen(false);
|
||||||
|
return handleConversationItemTop(item);
|
||||||
|
case 'unread':
|
||||||
|
setContextMenuOpen(false);
|
||||||
|
return handleConversationItemUnread(item);
|
||||||
|
case 'remark':
|
||||||
|
setOpenTags([]);
|
||||||
|
return;
|
||||||
|
case 'close':
|
||||||
|
setContextMenuOpen(false);
|
||||||
|
return handleConversationItemClose(item);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// setContextMenuOpen(false);
|
||||||
|
console.log('unknown key', key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}>
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
'border-0 border-t1 border-solid border-neutral-200',
|
||||||
|
String(item.sn) === String(currentConversation.sn) ? '__active text-primary bg-whatsapp-bg' : '',
|
||||||
|
String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '',
|
||||||
|
].join(' ')}>
|
||||||
|
{/* <div className='pl-4 pt-1 text-xs text-right'>
|
||||||
|
{tags.map((tag) => <Tag color={tag.color} key={tag.value}>{tag.label}</Tag>)}
|
||||||
|
</div> */}
|
||||||
|
<ChatItem
|
||||||
|
{...item}
|
||||||
|
key={item.sn}
|
||||||
|
id={item.sn}
|
||||||
|
letterItem={{ id: item.whatsapp_name || item.whatsapp_phone_number, letter: (item.whatsapp_name || item.whatsapp_phone_number).slice(0, 5) }}
|
||||||
|
alt={item.whatsapp_name}
|
||||||
|
title={item.whatsapp_name || item.whatsapp_phone_number}
|
||||||
|
// subtitle={item.coli_id}
|
||||||
|
subtitle={
|
||||||
|
<div>
|
||||||
|
{/* <ReadIcon /> */}
|
||||||
|
{/* <DeliverIcon /> */}
|
||||||
|
{/* <SentIcon /> */}
|
||||||
|
{/* todo: last message ⤴⤵↗️↖️↘✔️ */}
|
||||||
|
<span>{item.coli_id}</span>
|
||||||
|
<div className='text-sm'>
|
||||||
|
{[
|
||||||
|
{ label: '已付款', key: 'p1' },
|
||||||
|
{ label: '地接', key: 'p2' },
|
||||||
|
]?.map((tag) => (
|
||||||
|
<Tag key={tag.label} style={{ ...TagColorStyle(tag.label) }} className='text-xs px-0.5 me-0.5'>
|
||||||
|
{tag.label}
|
||||||
|
</Tag>
|
||||||
|
))}
|
||||||
|
<span title={'附加备注'}>附加备注</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
date={item.last_received_time || item.last_send_time}
|
||||||
|
unread={item.unread_msg_count > 99 ? 0 : item.unread_msg_count}
|
||||||
|
// className={[
|
||||||
|
// String(item.sn) === String(currentConversation.sn) ? '__active text-primary bg-whatsapp-bg' : '',
|
||||||
|
// String(item.sn) === String(tabSelectedConversation?.sn) ? ' bg-neutral-200' : '',
|
||||||
|
// ].join(' ')}
|
||||||
|
// statusText={<WhatsAppOutlined key={'channel'} className='text-whatsapp' />}
|
||||||
|
statusText={<ChannelLogo channel={'waba'} />}
|
||||||
|
statusColor={'#fff'}
|
||||||
|
onClick={() => onSwitchConversation(item)}
|
||||||
|
customStatusComponents={[
|
||||||
|
...(item.unread_msg_count > 99 ? [() => <div className='w-4 h-4 bg-red-500 rounded-full' key={'unread'}></div>] : []),
|
||||||
|
// () => <span key={'tag'} className='self-end>💎💴❤👑💼🤝💤💔💨✅🕳❓❔❇✳❎🚫❌🎈🎊🎁📜</span>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
export default ChatListItem;
|
Loading…
Reference in New Issue