触客宝右边栏提交

This commit is contained in:
wong
2025-08-30 17:07:53 +08:00
parent f6837f7819
commit b9d88160a2
5 changed files with 115 additions and 81 deletions

View File

@@ -18,7 +18,7 @@
.closeButton {
color: #8c8c8c;
&:hover {
color: #262626;
background: #f5f5f5;
@@ -44,7 +44,7 @@
font-size: 18px;
font-weight: 600;
color: #262626;
max-width: 200px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -52,12 +52,12 @@
.profileRemark {
margin-bottom: 12px;
.remarkText {
color: #8c8c8c;
font-size: 14px;
cursor: pointer;
&:hover {
color: #1890ff;
}
@@ -107,7 +107,7 @@
display: flex;
align-items: center;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
@@ -131,6 +131,20 @@
font-size: 14px;
flex: 1;
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;
}
}
}
@@ -169,7 +183,7 @@
@media (max-width: 768px) {
.profileSider {
width: 280px !important;
.profileContainer {
padding: 12px;
}
@@ -189,7 +203,7 @@
.infoItem {
margin-bottom: 10px;
.infoLabel {
width: 50px;
font-size: 13px;
@@ -201,4 +215,4 @@
}
}
}
}
}

View File

@@ -43,14 +43,33 @@ const Person: React.FC<PersonProps> = ({
const [messageApi, contextHolder] = message.useMessage();
const [isEditingRemark, setIsEditingRemark] = useState(false);
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(() => {
setRemarkValue(contract.conRemark || "");
setIsEditingRemark(false);
}, [contract.conRemark]);
setSelectedTags(contract.labels || []);
}, [contract.conRemark, contract.labels]);
// 处理备注保存
const handleSaveRemark = () => {
@@ -67,6 +86,18 @@ const Person: React.FC<PersonProps> = ({
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 = {
name: contract.name,
@@ -80,10 +111,10 @@ const Person: React.FC<PersonProps> = ({
department: contract.department || "-",
position: contract.position || "-",
company: contract.company || "-",
location: contract.location || "-",
region: contract.region || "-",
joinDate: contract.joinDate || "-",
status: "在线",
tags: contract.labels,
tags: selectedTags,
bio: contract.bio || "-",
};
@@ -123,8 +154,36 @@ const Person: React.FC<PersonProps> = ({
</h4>
</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 ? (
<div
style={{
@@ -163,7 +222,7 @@ const Person: React.FC<PersonProps> = ({
gap: "8px",
}}
>
<span className={styles.remarkText}>
<span>
{contractInfo.conRemark || "点击添加备注"}
</span>
<Button
@@ -175,73 +234,33 @@ const Person: React.FC<PersonProps> = ({
</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>
</Card>
{/* 标签 */}
<Card title="标签" className={styles.profileCard}>
<div className={styles.tagsContainer}>
{contractInfo.tags?.map((tag, index) => (
<Tag key={index} color="blue">
{tag}
</Tag>
))}
{/* 渲染所有可用标签,选中的排在前面 */}
{[...new Set([...selectedTags, ...allAvailableTags])].map((tag, index) => {
const isSelected = selectedTags.includes(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>
</Card>

View File

@@ -68,7 +68,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
>({});
const messagesEndRef = useRef<HTMLDivElement>(null);
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser());
const kfSelectedUser = useCkChatStore(state => state.kfSelectedUser(contract.wechatAccountId || 0));
useEffect(() => {
clearUnreadCount([contract.id]).then(() => {
setLoading(true);

View File

@@ -31,7 +31,7 @@ export interface CkChatState {
chatSessions: any[];
kfUserList: KfUserListData[];
kfSelected: number;
kfSelectedUser: () => KfUserListData | undefined;
kfSelectedUser: (kfId: number) => KfUserListData | undefined;
newContractList: { groupName: string; contacts: any[] }[];
asyncKfSelected: (data: number) => void;
getkfUserList: () => KfUserListData[];

View File

@@ -16,9 +16,10 @@ export const useCkChatStore = createPersistStore<CkChatState>(
kfUserList: [], //客服列表
kfSelected: 0,
newContractList: [], //联系人分组
kfSelectedUser: () => {
kfSelectedUser: (kfId:number) => {
const state = useCkChatStore.getState();
return state.kfUserList.find(item => item.id === state.kfSelected);
return state.kfUserList.find(item => item.id === (kfId));
},
asyncKfSelected: (data: number) => {
set({ kfSelected: data });