触客宝右边栏提交
This commit is contained in:
@@ -44,7 +44,7 @@
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #262626;
|
color: #262626;
|
||||||
max-width: 200px;
|
max-width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -131,6 +131,20 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
|
// 备注编辑区域样式
|
||||||
|
:global(.ant-input) {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.ant-btn) {
|
||||||
|
font-size: 12px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,14 +43,33 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
const [messageApi, contextHolder] = message.useMessage();
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const [isEditingRemark, setIsEditingRemark] = useState(false);
|
const [isEditingRemark, setIsEditingRemark] = useState(false);
|
||||||
const [remarkValue, setRemarkValue] = useState(contract.conRemark || "");
|
const [remarkValue, setRemarkValue] = useState(contract.conRemark || "");
|
||||||
|
const [selectedTags, setSelectedTags] = useState<string[]>(contract.labels || []);
|
||||||
|
const [allAvailableTags, setAllAvailableTags] = useState<string[]>([]);
|
||||||
|
|
||||||
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser());
|
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser(contract.wechatAccountId || 0));
|
||||||
|
|
||||||
// 当contract变化时更新备注值
|
// 获取所有可用标签
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAvailableTags = async () => {
|
||||||
|
try {
|
||||||
|
// 从kfSelectedUser.labels和contract.labels合并获取所有标签
|
||||||
|
const kfTags = kfSelectedUser?.labels || [];
|
||||||
|
const contractTags = contract.labels || [];
|
||||||
|
const allTags = [...new Set([...kfTags, ...contractTags])];
|
||||||
|
setAllAvailableTags(allTags);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取标签失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchAvailableTags();
|
||||||
|
}, [kfSelectedUser, contract.labels]);
|
||||||
|
|
||||||
|
// 当contract变化时更新备注值和标签
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRemarkValue(contract.conRemark || "");
|
setRemarkValue(contract.conRemark || "");
|
||||||
setIsEditingRemark(false);
|
setIsEditingRemark(false);
|
||||||
}, [contract.conRemark]);
|
setSelectedTags(contract.labels || []);
|
||||||
|
}, [contract.conRemark, contract.labels]);
|
||||||
|
|
||||||
// 处理备注保存
|
// 处理备注保存
|
||||||
const handleSaveRemark = () => {
|
const handleSaveRemark = () => {
|
||||||
@@ -67,6 +86,18 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
setIsEditingRemark(false);
|
setIsEditingRemark(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理标签点击切换
|
||||||
|
const handleTagToggle = (tagName: string) => {
|
||||||
|
const newSelectedTags = selectedTags.includes(tagName)
|
||||||
|
? selectedTags.filter(tag => tag !== tagName)
|
||||||
|
: [...selectedTags, tagName];
|
||||||
|
|
||||||
|
setSelectedTags(newSelectedTags);
|
||||||
|
|
||||||
|
// 这里应该调用API保存标签到后端
|
||||||
|
messageApi.success(`标签"${tagName}"${selectedTags.includes(tagName) ? '已取消' : '已选中'}`);
|
||||||
|
};
|
||||||
|
|
||||||
// 模拟联系人详细信息
|
// 模拟联系人详细信息
|
||||||
const contractInfo = {
|
const contractInfo = {
|
||||||
name: contract.name,
|
name: contract.name,
|
||||||
@@ -80,10 +111,10 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
department: contract.department || "-",
|
department: contract.department || "-",
|
||||||
position: contract.position || "-",
|
position: contract.position || "-",
|
||||||
company: contract.company || "-",
|
company: contract.company || "-",
|
||||||
location: contract.location || "-",
|
region: contract.region || "-",
|
||||||
joinDate: contract.joinDate || "-",
|
joinDate: contract.joinDate || "-",
|
||||||
status: "在线",
|
status: "在线",
|
||||||
tags: contract.labels,
|
tags: selectedTags,
|
||||||
bio: contract.bio || "-",
|
bio: contract.bio || "-",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,8 +154,36 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
</h4>
|
</h4>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<div className={styles.profileRemark}>
|
|
||||||
{JSON.stringify(kfSelectedUser)}
|
|
||||||
|
<div className={styles.profileStatus}>
|
||||||
|
<span className={styles.statusDot}></span>
|
||||||
|
{contractInfo.status}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 详细信息卡片 */}
|
||||||
|
<Card title="详细信息" className={styles.profileCard}>
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<TeamOutlined className={styles.infoIcon} />
|
||||||
|
<span className={styles.infoLabel}>微信号:</span>
|
||||||
|
<span className={styles.infoValue}>{contractInfo.alias || contractInfo.wechatId}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<PhoneOutlined className={styles.infoIcon} />
|
||||||
|
<span className={styles.infoLabel}>电话:</span>
|
||||||
|
<span className={styles.infoValue}>{contractInfo.phone}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<EnvironmentOutlined className={styles.infoIcon} />
|
||||||
|
<span className={styles.infoLabel}>地区:</span>
|
||||||
|
<span className={styles.infoValue}>{contractInfo.region}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.infoItem}>
|
||||||
|
<EditOutlined className={styles.infoIcon} />
|
||||||
|
<span className={styles.infoLabel}>备注:</span>
|
||||||
|
<div className={styles.infoValue}>
|
||||||
{isEditingRemark ? (
|
{isEditingRemark ? (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@@ -163,7 +222,7 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
gap: "8px",
|
gap: "8px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className={styles.remarkText}>
|
<span>
|
||||||
{contractInfo.conRemark || "点击添加备注"}
|
{contractInfo.conRemark || "点击添加备注"}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
@@ -175,73 +234,33 @@ const Person: React.FC<PersonProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.profileStatus}>
|
|
||||||
<span className={styles.statusDot}></span>
|
|
||||||
{contractInfo.status}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 详细信息卡片 */}
|
|
||||||
<Card title="详细信息" className={styles.profileCard}>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<TeamOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>微信号:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.wechatId}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<UserOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>昵称:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.alias}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<PhoneOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>电话:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.phone}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<MailOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>邮箱:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.email}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<BankOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>部门:</span>
|
|
||||||
<span className={styles.infoValue}>
|
|
||||||
{contractInfo.department}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<StarOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>职位:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.position}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<BankOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>公司:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.company}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<EnvironmentOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>地区:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.location}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.infoItem}>
|
|
||||||
<CalendarOutlined className={styles.infoIcon} />
|
|
||||||
<span className={styles.infoLabel}>入职时间:</span>
|
|
||||||
<span className={styles.infoValue}>{contractInfo.joinDate}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* 标签 */}
|
{/* 标签 */}
|
||||||
<Card title="标签" className={styles.profileCard}>
|
<Card title="标签" className={styles.profileCard}>
|
||||||
<div className={styles.tagsContainer}>
|
<div className={styles.tagsContainer}>
|
||||||
{contractInfo.tags?.map((tag, index) => (
|
{/* 渲染所有可用标签,选中的排在前面 */}
|
||||||
<Tag key={index} color="blue">
|
{[...new Set([...selectedTags, ...allAvailableTags])].map((tag, index) => {
|
||||||
{tag}
|
const isSelected = selectedTags.includes(tag);
|
||||||
</Tag>
|
return (
|
||||||
))}
|
<Tag
|
||||||
|
key={index}
|
||||||
|
color={isSelected ? "blue" : "default"}
|
||||||
|
style={{
|
||||||
|
cursor: 'pointer',
|
||||||
|
border: isSelected ? '1px solid #1890ff' : '1px solid #d9d9d9',
|
||||||
|
backgroundColor: isSelected ? '#e6f7ff' : '#fafafa'
|
||||||
|
}}
|
||||||
|
onClick={() => handleTagToggle(tag)}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{allAvailableTags.length === 0 && (
|
||||||
|
<span style={{ color: '#999', fontSize: '12px' }}>暂无可用标签</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
|
|||||||
>({});
|
>({});
|
||||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser());
|
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser(contract.wechatAccountId || 0));
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearUnreadCount([contract.id]).then(() => {
|
clearUnreadCount([contract.id]).then(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export interface CkChatState {
|
|||||||
chatSessions: any[];
|
chatSessions: any[];
|
||||||
kfUserList: KfUserListData[];
|
kfUserList: KfUserListData[];
|
||||||
kfSelected: number;
|
kfSelected: number;
|
||||||
kfSelectedUser: () => KfUserListData | undefined;
|
kfSelectedUser: (kfId: number) => KfUserListData | undefined;
|
||||||
newContractList: { groupName: string; contacts: any[] }[];
|
newContractList: { groupName: string; contacts: any[] }[];
|
||||||
asyncKfSelected: (data: number) => void;
|
asyncKfSelected: (data: number) => void;
|
||||||
getkfUserList: () => KfUserListData[];
|
getkfUserList: () => KfUserListData[];
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ export const useCkChatStore = createPersistStore<CkChatState>(
|
|||||||
kfUserList: [], //客服列表
|
kfUserList: [], //客服列表
|
||||||
kfSelected: 0,
|
kfSelected: 0,
|
||||||
newContractList: [], //联系人分组
|
newContractList: [], //联系人分组
|
||||||
kfSelectedUser: () => {
|
kfSelectedUser: (kfId:number) => {
|
||||||
const state = useCkChatStore.getState();
|
const state = useCkChatStore.getState();
|
||||||
return state.kfUserList.find(item => item.id === state.kfSelected);
|
|
||||||
|
return state.kfUserList.find(item => item.id === (kfId));
|
||||||
},
|
},
|
||||||
asyncKfSelected: (data: number) => {
|
asyncKfSelected: (data: number) => {
|
||||||
set({ kfSelected: data });
|
set({ kfSelected: data });
|
||||||
|
|||||||
Reference in New Issue
Block a user