feat: 编辑会话; 联系方式相关字段有值时不允许修改; todo: 会话列表显示

dev/email
Lei OT 12 months ago
parent f7c717c5c1
commit 3f244729f1

@ -1,19 +1,15 @@
import React, { useEffect, useState, useRef } from 'react';
import React, { useEffect, useState } 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 { Dropdown, Input, Button, Tag, Popover, Form } from 'antd';
import { CloseCircleOutlined } from '@ant-design/icons';
import { fetchConversationItemClose, fetchConversationsSearch, fetchConversationItemUnread, fetchConversationItemTop, postConversationTags, deleteConversationTags } from '@/actions/ConversationActions';
import { ChatItem } from 'react-chat-elements';
// import ConversationsNewItem from './ConversationsNewItem';
import { isEmpty, olog, stringToColour } from '@/utils/commons';
import { isEmpty, 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;
import { DeliverIcon, ReadIcon, SentIcon } from '@/components/Icons';
const TagColorStyle = (tag) => {
const color = stringToColour(tag);
@ -95,7 +91,7 @@ const EditChatMemoForm = ({onSubmit,...props}) => {
};
const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,onSwitchConversation,tabSelectedConversation, ...props}) => {
const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,onSwitchConversation,tabSelectedConversation, setNewChatModalVisible,setEditingChat,...props}) => {
const routerReplace = mobile === undefined ? true : false; // : true;
const routePrefix = mobile === undefined ? `/order/chat` : `/m/chat`;
const { state: orderRow } = useLocation();
@ -170,6 +166,7 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
key={item.sn}
destroyPopupOnHide
trigger={['contextMenu']}
overlayClassName='z-[998]'
open={contextMenuOpen}
onOpenChange={handleContextMenuOpenChange}
menu={{
@ -205,19 +202,20 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
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',
},
{ label: '编辑联系人', key: 'edit0' },
// {
// 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 },
],
@ -241,12 +239,16 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
case 'unread':
setContextMenuOpen(false);
return handleConversationItemUnread(item);
case 'remark':
setOpenTags([]);
return;
// case 'remark':
// setOpenTags([]);
// return;
case 'close':
setContextMenuOpen(false);
return handleConversationItemClose(item);
case 'edit0':
setOpenTags([]);
setEditingChat({...item, is_new: false});
return setNewChatModalVisible(true);
default:
// setContextMenuOpen(false);
@ -271,7 +273,7 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
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}
title={'备注 / 名称' || item.whatsapp_name || item.whatsapp_phone_number}
// subtitle={item.coli_id}
subtitle={
<div>
@ -279,7 +281,9 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
{/* <DeliverIcon /> */}
{/* <SentIcon /> */}
{/* todo: last message ⤴⤵↗️↖️↘✔️ */}
<span>{item.coli_id}</span>
{/* <span>{item.coli_id}</span> */}
<span><ReadIcon />最后一条消息</span>
{/* <span>最后一条消息</span> */}
<div className='text-sm'>
{[
{ label: '已付款', key: 'p1' },
@ -289,7 +293,7 @@ const ChatListItem = (({mobile, item, refreshConversationList,setListUpdateFlag,
{tag.label}
</Tag>
))}
<span title={'附加备注'}>附加备注</span>
{/* <span title={'附加备注'}>附加备注</span> */}
</div>
</div>
}

@ -185,6 +185,7 @@ const Conversations = ({ mobile }) => {
}
const [newChatModalVisible, setNewChatModalVisible] = useState(false);
const [editingChat, setEditingChat] = useState({});
// const closedVisible = closedConversationsList.length > 0;
const toggleClosedConversationsList = () => {
@ -295,95 +296,12 @@ const Conversations = ({ mobile }) => {
) : null}
{dataSource.map((item) => (
<ChatListItem key={item.sn} {...{mobile, item, refreshConversationList,setListUpdateFlag,onSwitchConversation,tabSelectedConversation}} />
// <Dropdown
// key={item.sn}
// open={contextMenuOpen}
// onOpenChange={handleContextMenuOpenChange}
// menu={{
// items: [
// { label: '', key: 'top' },
// // { label: '', key: 'no_top' },
// { label: '', key: 'unread' },
// { label: '', key: 'tags', children: [...custom_tags.map((t) => ({ ...t, style: { color: stringToColour(t.label) } })),
// { label: (<>
// <Popover content={<Input placeholder='' />}>
// <Button type='dashed' size='small' className='m-1'>+</Button>
// </Popover>
// </>), key: 'new_tags' },
// ] }, // selection
// { label: '', key: 'remark' },
// { type: 'divider' },
// { label: '', key: 'close', danger: true },
// ],
// onClick: ({ key, domEvent }) => {
// domEvent.stopPropagation();
// switch (key) {
// case 'close':
// setContextMenuOpen(false);
// return handleConversationItemClose(item);
// case 'unread':
// setContextMenuOpen(false);
// return handleConversationItemUnread(item);
// default:
// setContextMenuOpen(false);
// return;
// }
// },
// }}
// trigger={['contextMenu']}>
// <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'>{/* {filterTags.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>
// {item.coli_id}
// <div>
// {[
// { 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>
// ))}
// </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' />}
// 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'}>💎💴👑💼🤝💤💔💨🕳🚫🎈🎊🎁📜</span>,
// ]}
// />
// </div>
// </Dropdown>
<ChatListItem key={item.sn} {...{mobile, item, refreshConversationList,setListUpdateFlag,onSwitchConversation,tabSelectedConversation,setNewChatModalVisible,setEditingChat}} />
))}
{dataSource.length === 0 && <Empty description={'无数据'} />}
</div>
<ConversationsNewItem
initialValues={{ is_current_order: false }}
initialValues={{ ...editingChat, is_current_order: false }}
open={newChatModalVisible}
onCreate={() => setNewChatModalVisible(false)}
onCancel={() => setNewChatModalVisible(false)}

@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { Form, Input, Modal } from 'antd';
import { isEmpty, pick } from '@/utils/commons';
import { isEmpty, isNotEmpty, pick } from '@/utils/commons';
import useConversationStore from '@/stores/ConversationStore';
import { phoneNumberToWAID } from '@/channel/whatsappUtils';
import { useConversationNewItem } from '@/hooks/useConversation';
@ -30,18 +30,31 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
}
};
return (
<Form layout='horizontal' form={form} name='form_in_modal' initialValues={{ ...fromCurrent, ...initialValues, wa_id: formatPhone }} onValuesChange={onValuesChange}>
<Form layout='horizontal' form={form} name='form_in_modal' initialValues={{ ...fromCurrent, ...initialValues, wa_id: formatPhone }} onValuesChange={onValuesChange} labelCol={{span: 4}} labelWrap={true}>
{/* {!initialValues.is_current_order && ( */}
<Form.Item name={'name'} label='名称' rules={[{ required: true, message: '请输入联系人名称' }, { max: 20, message: '最长20字' }]} >
<Input placeholder='请输入联系人名称' />
</Form.Item>
{/* )} */}
<Form.Item name={'remark'} label='备注' rules={[{type: 'string', max: 50, message: '备注最多50字' }]}>
<Input />
</Form.Item>
<Form.Item
name={'phone_number'}
label='WhatsApp号码'
label='手机'
validateStatus={formatVisible ? 'warning' : undefined}
help={formatVisible ? `WhatsApp格式: ${formatPhone}` : undefined}
rules={[{ required: true, message: '请输入联系人手机号' }]}>
<Input placeholder='请输入联系人手机号' />
<Input placeholder='请输入联系人手机号' disabled={!initialValues.is_new && isNotEmpty(initialValues.phone_number)} />
</Form.Item>
{/* hidden */}
<Form.Item hidden name={'wa_id'} dependencies={['phone_number']}>
<Form.Item label='WhatsApp' name={'wa_id'} dependencies={['phone_number']} >
<Input disabled={!initialValues.is_new && isNotEmpty(initialValues.wa_id)} />
</Form.Item>
{/* <Form.Item name={'phone'} label=''>
<Input />
</Form.Item> */}
<Form.Item name={'email'} label='邮箱' rules={[{ type: 'email', message: '请输入正确的邮箱地址' }]}>
<Input disabled={!initialValues.is_new && isNotEmpty(initialValues.email)} />
</Form.Item>
{initialValues.is_current_order && (
<>
@ -58,11 +71,6 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
</Form.Item>
</>
)}
{!initialValues.is_current_order && (
<Form.Item name={'name'} label='联系人名称' rules={[{ required: true, message: '请输入联系人名称' }]} className='mb-1'>
<Input placeholder='请输入联系人名称' />
</Form.Item>
)}
{/* <div className=' text-neutral-500 italic'>如果会话已存在, 将直接切换</div> */}
</Form>
);
@ -73,16 +81,30 @@ export const ConversationItemForm = ({ initialValues, onFormInstanceReady }) =>
* * 消息记录中的号码点击: 不自动关联
* * 消息记录中的联系人卡片点击: 不自动关联
*/
export const ConversationItemFormModal = ({ open, onCreate, onCancel, initialValues, }) => {
export const ConversationItemFormModal = ({ open, onCreate, onCancel, initialValues: _initialValues, }) => {
const [formInstance, setFormInstance] = useState();
const [newItemLoading, setNewItemLoading] = useState(false);
const { newConversation } = useConversationNewItem();
const [initialValues, setInitialValues] = useState({});
useEffect(() => {
setInitialValues({
..._initialValues,
phone_number: _initialValues?.whatsapp_phone_number || _initialValues?.phone_number || '',
wa_id: _initialValues?.whatsapp_phone_number || _initialValues?.wa_id || '',
name: _initialValues?.whatsapp_name || _initialValues?.name || '',
is_new: _initialValues?.is_new ?? true,
});
return () => {};
}, [_initialValues])
return (
<Modal
open={open}
title='新建会话'
title={initialValues.is_new ? '新建会话' : '编辑会话'}
okText='确认'
// cancelText='Cancel'
okButtonProps={{

@ -144,7 +144,7 @@ const CustomerProfile = () => {
size={"small"}
onClick={() => {
setNewChatModalVisible(true);
setNewChatFormValues(prev => ({ ...prev, phone_number: customerDetail.whatsapp_phone_number, is_current_order: true }));
setNewChatFormValues(prev => ({ ...prev, phone_number: customerDetail.whatsapp_phone_number, name: customerDetail.name, is_current_order: true }));
}}>
{customerDetail.whatsapp_phone_number}
</Button>

Loading…
Cancel
Save