触客宝右边栏提交
This commit is contained in:
@@ -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 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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 });
|
||||
|
||||
Reference in New Issue
Block a user