|
|
@@ -22,6 +22,13 @@
|
|
|
<span class="stat-item" @click="openRelationDialog('fans')">{{ userInfo.likeCount }} 粉丝</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <el-button type="primary" size="small" @click="addFriend" class="add-friend-btn">
|
|
|
+ <el-icon class="btn-icon">
|
|
|
+ <Plus />
|
|
|
+ </el-icon>
|
|
|
+ 添加好友
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -453,7 +460,7 @@
|
|
|
<div v-for="user in filteredRelationList" :key="user.id" class="relation-item">
|
|
|
<div class="user-info-row">
|
|
|
<div class="user-avatar-small">
|
|
|
- <img v-if="user.avatar" :src="user.avatar" :alt="user.name" />
|
|
|
+ <img v-if="user.image" :src="user.avatar" :alt="user.name" />
|
|
|
<el-icon v-else :size="40">
|
|
|
<Avatar />
|
|
|
</el-icon>
|
|
|
@@ -463,7 +470,7 @@
|
|
|
{{ user.name }}
|
|
|
</div>
|
|
|
<div class="user-desc">
|
|
|
- {{ user.description }}
|
|
|
+ {{ user.blurb }}
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -490,6 +497,78 @@
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
|
|
|
+ <!-- 添加好友对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="addFriendDialogVisible"
|
|
|
+ title="添加好友"
|
|
|
+ width="500px"
|
|
|
+ destroy-on-close
|
|
|
+ @close="handleCloseAddFriendDialog"
|
|
|
+ >
|
|
|
+ <div class="add-friend-dialog-content">
|
|
|
+ <!-- 搜索框 -->
|
|
|
+ <div class="search-box">
|
|
|
+ <el-input
|
|
|
+ v-model="addFriendSearchKeyword"
|
|
|
+ placeholder="请输入手机号或昵称"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleSearchFriend"
|
|
|
+ @input="handleSearchInput"
|
|
|
+ @clear="handleClearSearch"
|
|
|
+ >
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon>
|
|
|
+ <Search />
|
|
|
+ </el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 搜索结果 -->
|
|
|
+ <div v-if="addFriendSearchKeyword" class="search-results">
|
|
|
+ <div v-if="addFriendSearching" class="search-loading">
|
|
|
+ <el-icon class="is-loading" :size="20">
|
|
|
+ <Loading />
|
|
|
+ </el-icon>
|
|
|
+ <span>搜索中...</span>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="addFriendSearchResults.length > 0" class="search-results-list">
|
|
|
+ <div class="search-results-count">共{{ addFriendSearchResults.length }}个搜索结果</div>
|
|
|
+ <div v-for="user in addFriendSearchResults" :key="user.phoneId || user.id" class="search-result-item">
|
|
|
+ <div class="user-info-row">
|
|
|
+ <div class="user-avatar-small">
|
|
|
+ <img v-if="user.imgUrl" :src="user.imgUrl || user.userAvatar" :alt="user.name || user.userName" />
|
|
|
+ <el-icon v-else :size="40">
|
|
|
+ <Avatar />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="user-details">
|
|
|
+ <div class="user-name-text">
|
|
|
+ {{ user.storeUserName || "—" }}
|
|
|
+ </div>
|
|
|
+ <div class="user-desc">
|
|
|
+ {{ user.blurb || "—" }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="action-button">
|
|
|
+ <el-button v-if="user.isFollowThis == 1" type="primary" plain size="small" @click="handleUnfollowInSearch(user)">
|
|
|
+ 已关注
|
|
|
+ </el-button>
|
|
|
+ <el-button v-else type="primary" plain size="small" @click="handleFollowInSearch(user)"> 关注 </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="search-empty">
|
|
|
+ <el-empty description="暂无搜索结果" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else class="search-placeholder">
|
|
|
+ <el-empty description="请输入手机号或昵称进行搜索" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
<!-- 举报对话框 -->
|
|
|
<el-dialog v-model="reportDialogVisible" title="举报理由" width="500px" destroy-on-close @close="handleCloseReportDialog">
|
|
|
<div class="report-dialog-content">
|
|
|
@@ -638,7 +717,8 @@ import {
|
|
|
Warning,
|
|
|
Plus,
|
|
|
CircleCheck,
|
|
|
- CircleClose
|
|
|
+ CircleClose,
|
|
|
+ Loading
|
|
|
} from "@element-plus/icons-vue";
|
|
|
import {
|
|
|
deleteDynamicsById,
|
|
|
@@ -654,7 +734,10 @@ import {
|
|
|
getMyUserFans,
|
|
|
blockUser,
|
|
|
likeDynamicNew,
|
|
|
- unlikeDynamicNew
|
|
|
+ unlikeDynamicNew,
|
|
|
+ getStoreAndUserByName,
|
|
|
+ toggleFollowUser,
|
|
|
+ cancelFollewed
|
|
|
} from "@/api/modules/newLoginApi";
|
|
|
import {} from "@/api/modules/dynamicManagement";
|
|
|
import { useUserStore } from "@/stores/modules/user";
|
|
|
@@ -788,6 +871,12 @@ const shareSearch = ref("");
|
|
|
const shareFriendList = ref<ShareFriend[]>([]);
|
|
|
const selectedFriends = ref<number[]>([]);
|
|
|
|
|
|
+// 添加好友对话框相关
|
|
|
+const addFriendDialogVisible = ref(false);
|
|
|
+const addFriendSearchKeyword = ref("");
|
|
|
+const addFriendSearchResults = ref<any[]>([]);
|
|
|
+const addFriendSearching = ref(false);
|
|
|
+
|
|
|
// 过滤后的好友列表
|
|
|
const filteredShareFriendList = computed(() => {
|
|
|
if (!shareSearch.value) {
|
|
|
@@ -1638,16 +1727,9 @@ const handleFriendList = async () => {
|
|
|
fansId: fansId,
|
|
|
name: relationSearch.value || ""
|
|
|
});
|
|
|
- if (res.code === 200) {
|
|
|
- const dataList = res.data?.records || res.data?.list || res.data || [];
|
|
|
- relationList.value = dataList.map((item: any) => ({
|
|
|
- id: item.id || item.userId,
|
|
|
- name: item.userName || item.nickname || item.name || "用户",
|
|
|
- avatar: item.userImage || item.avatar || item.headImg || "",
|
|
|
- description: item.description || item.bio || item.signature || "欢迎来这里",
|
|
|
- relationStatus: "mutual" as const, // 好友列表都是互相关注
|
|
|
- phoneId: item.phoneId || item.fansId || "" // 保存 phoneId 用于后续操作
|
|
|
- }));
|
|
|
+ if (res.code == 200) {
|
|
|
+ const dataList = res.data?.records || [];
|
|
|
+ relationList.value = dataList;
|
|
|
}
|
|
|
};
|
|
|
//关注列表
|
|
|
@@ -1662,14 +1744,7 @@ const handleFollowList = async () => {
|
|
|
});
|
|
|
if (res.code === 200) {
|
|
|
const dataList = res.data?.records || res.data?.list || res.data || [];
|
|
|
- relationList.value = dataList.map((item: any) => ({
|
|
|
- id: item.id || item.userId,
|
|
|
- name: item.userName || item.nickname || item.name || "用户",
|
|
|
- avatar: item.userImage || item.avatar || item.headImg || "",
|
|
|
- description: item.description || item.bio || item.signature || "欢迎来这里",
|
|
|
- relationStatus: "following" as const, // 关注列表都是已关注状态
|
|
|
- phoneId: item.phoneId || item.followedId || "" // 保存 phoneId 用于后续操作
|
|
|
- }));
|
|
|
+ relationList.value = dataList;
|
|
|
}
|
|
|
};
|
|
|
//粉丝列表
|
|
|
@@ -1755,6 +1830,156 @@ const handleCloseRelationDialog = () => {
|
|
|
relationList.value = [];
|
|
|
};
|
|
|
|
|
|
+// 打开添加好友对话框
|
|
|
+const addFriend = () => {
|
|
|
+ addFriendDialogVisible.value = true;
|
|
|
+};
|
|
|
+
|
|
|
+// 关闭添加好友对话框
|
|
|
+const handleCloseAddFriendDialog = () => {
|
|
|
+ // 清空防抖定时器
|
|
|
+ if (searchDebounceTimer) {
|
|
|
+ clearTimeout(searchDebounceTimer);
|
|
|
+ searchDebounceTimer = null;
|
|
|
+ }
|
|
|
+ addFriendSearchKeyword.value = "";
|
|
|
+ addFriendSearchResults.value = [];
|
|
|
+ addFriendSearching.value = false;
|
|
|
+};
|
|
|
+
|
|
|
+// 搜索输入防抖定时器
|
|
|
+let searchDebounceTimer: NodeJS.Timeout | null = null;
|
|
|
+
|
|
|
+// 搜索输入处理(带防抖)
|
|
|
+const handleSearchInput = () => {
|
|
|
+ // 清空之前的定时器
|
|
|
+ if (searchDebounceTimer) {
|
|
|
+ clearTimeout(searchDebounceTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果输入框为空,清空搜索结果
|
|
|
+ if (!addFriendSearchKeyword.value.trim()) {
|
|
|
+ addFriendSearchResults.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置防抖,500ms后执行搜索
|
|
|
+ searchDebounceTimer = setTimeout(() => {
|
|
|
+ handleSearchFriend();
|
|
|
+ }, 200);
|
|
|
+};
|
|
|
+
|
|
|
+// 搜索好友
|
|
|
+const handleSearchFriend = async () => {
|
|
|
+ if (!addFriendSearchKeyword.value.trim()) {
|
|
|
+ addFriendSearchResults.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ addFriendSearching.value = true;
|
|
|
+ addFriendSearchResults.value = [];
|
|
|
+
|
|
|
+ try {
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+ const userId = userStore.userInfo?.id || userStore.userInfo?.userId || "";
|
|
|
+
|
|
|
+ const res: any = await getStoreAndUserByName({
|
|
|
+ page: 1,
|
|
|
+ size: 100,
|
|
|
+ phoneId: currentUserPhoneId,
|
|
|
+ userId: userId,
|
|
|
+ searchName: addFriendSearchKeyword.value.trim()
|
|
|
+ });
|
|
|
+
|
|
|
+ if (res && res.code === 200) {
|
|
|
+ // 处理搜索结果数据
|
|
|
+ const dataList = res.data?.records || [];
|
|
|
+ addFriendSearchResults.value = dataList;
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res?.msg || res?.message || "搜索失败");
|
|
|
+ }
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("搜索好友失败:", error);
|
|
|
+ ElMessage.error(error?.message || "搜索失败,请重试");
|
|
|
+ } finally {
|
|
|
+ addFriendSearching.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 清空搜索
|
|
|
+const handleClearSearch = () => {
|
|
|
+ // 清空防抖定时器
|
|
|
+ if (searchDebounceTimer) {
|
|
|
+ clearTimeout(searchDebounceTimer);
|
|
|
+ searchDebounceTimer = null;
|
|
|
+ }
|
|
|
+ addFriendSearchResults.value = [];
|
|
|
+};
|
|
|
+
|
|
|
+// 在搜索结果中关注用户
|
|
|
+const handleFollowInSearch = async (user: any) => {
|
|
|
+ try {
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+
|
|
|
+ if (!user.phoneId) {
|
|
|
+ ElMessage.error("用户信息不完整");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await toggleFollowUser({
|
|
|
+ followedId: user.phoneId,
|
|
|
+ fansId: currentUserPhoneId,
|
|
|
+ fansType: 2
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新关注状态
|
|
|
+ user.isFollowed = 1;
|
|
|
+ user.isFollowThis = 1;
|
|
|
+
|
|
|
+ ElMessage.success("关注成功");
|
|
|
+ } catch (error: any) {
|
|
|
+ console.error("关注失败:", error);
|
|
|
+ ElMessage.error(error?.message || "关注失败,请重试");
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 在搜索结果中取消关注用户
|
|
|
+const handleUnfollowInSearch = async (user: any) => {
|
|
|
+ try {
|
|
|
+ await ElMessageBox.confirm("确定要取消关注吗?", "提示", {
|
|
|
+ confirmButtonText: "确定",
|
|
|
+ cancelButtonText: "取消",
|
|
|
+ type: "warning"
|
|
|
+ });
|
|
|
+
|
|
|
+ const phone = userStore.userInfo?.phone || "";
|
|
|
+ const currentUserPhoneId = phone.startsWith("store_") ? phone : `store_${phone}`;
|
|
|
+
|
|
|
+ if (!user.phoneId) {
|
|
|
+ ElMessage.error("用户信息不完整");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await cancelFollewed({
|
|
|
+ followedId: user.phoneId,
|
|
|
+ fansId: currentUserPhoneId
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新关注状态
|
|
|
+ user.isFollowed = 0;
|
|
|
+ user.isFollowThis = 0;
|
|
|
+
|
|
|
+ ElMessage.success("已取消关注");
|
|
|
+ } catch (error: any) {
|
|
|
+ if (error !== "cancel") {
|
|
|
+ console.error("取消关注失败:", error);
|
|
|
+ ElMessage.error(error?.message || "取消关注失败,请重试");
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 初始化
|
|
|
onMounted(() => {
|
|
|
loadUserInfo();
|
|
|
@@ -2115,6 +2340,15 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ .add-friend-btn {
|
|
|
+ display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-items: center;
|
|
|
+ margin-left: auto;
|
|
|
+ .btn-icon {
|
|
|
+ margin-right: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
.user-bio {
|
|
|
@@ -2910,6 +3144,99 @@ onMounted(() => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ // 添加好友对话框样式
|
|
|
+ .add-friend-dialog-content {
|
|
|
+ .search-box {
|
|
|
+ margin-bottom: 20px;
|
|
|
+ }
|
|
|
+ .search-results {
|
|
|
+ max-height: 400px;
|
|
|
+ overflow-y: auto;
|
|
|
+ .search-loading {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 40px 0;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ .search-results-count {
|
|
|
+ padding: 0 4px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ .search-results-list {
|
|
|
+ .search-result-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 16px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ transition: background-color 0.2s;
|
|
|
+ &:hover {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ }
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+ .user-info-row {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ .user-avatar-small {
|
|
|
+ display: flex;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ overflow: hidden;
|
|
|
+ background-color: #f0f0f0;
|
|
|
+ border-radius: 50%;
|
|
|
+ img {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .user-details {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ .user-name-text {
|
|
|
+ margin-bottom: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ .user-desc {
|
|
|
+ overflow: hidden;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .action-button {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-left: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .search-empty {
|
|
|
+ padding: 40px 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .search-placeholder {
|
|
|
+ padding: 40px 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|
|
|
|