Przeglądaj źródła

update views/chat

reghao 1 tydzień temu
rodzic
commit
804e8a8eee

+ 5 - 6
src/api/chat.js

@@ -3,8 +3,7 @@ import { get, post, delete0 } from '@/utils/request'
 const chatApi = {
   contactApi: '/api/chat/contact',
   chatRecordApi: '/api/chat/talk/records',
-  dialogueApi: '/api/chat/dialogue',
-  userApi: '/api/chat/user'
+  dialogueApi: '/api/chat/dialogue'
 }
 
 export function getContacts() {
@@ -15,6 +14,10 @@ export function getChatRecords(queryParams) {
   return get(chatApi.chatRecordApi, queryParams)
 }
 
+export function searchChatRecords(queryParams) {
+  return get(chatApi.chatRecordApi + '/search', queryParams)
+}
+
 export function getChatUnreadCount() {
   return get(chatApi.dialogueApi + '/unread_total')
 }
@@ -30,7 +33,3 @@ export function getChatDialogue(queryParams) {
 export function updateChatDialogue(payload) {
   return post(chatApi.dialogueApi + '/update', payload)
 }
-
-export function getChatUser(queryParams) {
-  return get(chatApi.userApi, queryParams)
-}

+ 299 - 7
src/views/chat/Chat.vue

@@ -1,17 +1,77 @@
 <template>
   <div class="app-container">
     <header v-if="$route.path !== '/chat/dialogue'" class="app-header">
-      <span class="title">{{ currentTitle }}</span>
-      <div class="header-icons">
-        <i class="el-icon-search" />
-        <i class="el-icon-plus" style="margin-left: 15px;" />
-      </div>
+      <template v-if="!isSearching">
+        <span class="title">{{ currentTitle }}</span>
+        <div class="header-icons">
+          <i class="el-icon-search" @click="openSearch" />
+          <i class="el-icon-plus" style="margin-left: 15px;" @click="openPlus" />
+        </div>
+      </template>
+
+      <template v-else>
+        <div class="search-bar-wrapper">
+          <el-input
+            ref="searchInput"
+            v-model="searchQuery"
+            prefix-icon="el-icon-search"
+            placeholder="搜索"
+            size="small"
+            clearable
+            class="wechat-search-input"
+            @input="handleSearchInput"
+          />
+          <span class="cancel-btn" @click="closeSearch">取消</span>
+        </div>
+      </template>
     </header>
 
     <main class="app-content">
       <keep-alive>
         <router-view />
       </keep-alive>
+
+      <transition name="fade">
+        <div v-if="isSearching && searchQuery.trim()" class="search-result-panel">
+          <div v-if="filteredContacts.length > 0" class="result-group">
+            <div class="group-title">联系人</div>
+            <div
+              v-for="item in filteredContacts"
+              :key="'contact-' + item.id"
+              class="result-item"
+              @click="goToDialogue(item)"
+            >
+              <img :src="item.avatarUrl" class="result-avatar">
+              <div class="result-info">
+                <div class="result-name" v-html="highlightText(item.screenName, searchQuery)" />
+              </div>
+            </div>
+          </div>
+
+          <div v-if="filteredMessages.length > 0" class="result-group">
+            <div class="group-title">聊天记录</div>
+            <div
+              v-for="item in filteredMessages"
+              :key="'msg-' + item.messageId"
+              class="result-item"
+              @click="goToDialogue(item)"
+            >
+              <img :src="item.sender.avatarUrl" class="result-avatar">
+              <div class="result-info">
+                <div class="result-meta">
+                  <span class="result-name">{{ item.sender.screenName }}</span>
+                  <span class="result-time">{{ item.createdAt }}</span>
+                </div>
+                <div class="result-text" v-html="highlightText(item.content, searchQuery)" />
+              </div>
+            </div>
+          </div>
+
+          <div v-if="filteredContacts.length === 0 && filteredMessages.length === 0" class="no-result">
+            无法找到“{{ searchQuery }}”相关内容
+          </div>
+        </div>
+      </transition>
     </main>
 
     <footer v-if="$route.path !== '/chat/dialogue'" class="app-footer">
@@ -45,13 +105,19 @@
 </template>
 
 <script>
-import { getChatUnreadCount } from '@/api/chat'
+import { getChatUnreadCount, searchChatRecords } from '@/api/chat'
 
 export default {
   name: 'Chat',
   data() {
     return {
-      unreadCount: 0
+      unreadCount: 0,
+      isSearching: false, // 是否处于搜索激活状态
+      searchQuery: '', // 搜索关键词
+      searchTimer: null, // 防抖定时器,
+      // ==== 模拟全局本地搜索数据库 ====
+      contactResult: [],
+      messageResult: []
     }
   },
   computed: {
@@ -60,6 +126,14 @@ export default {
       if (this.$route.path.includes('contact')) return '通讯录'
       if (this.$route.path.includes('me')) return '我'
       return '微信'
+    },
+    // 动态计算筛选后的联系人
+    filteredContacts() {
+      return this.contactResult
+    },
+    // 动态计算筛选后的聊天记录
+    filteredMessages() {
+      return this.messageResult
     }
   },
   created() {
@@ -74,6 +148,86 @@ export default {
       if (this.$route.path !== path) {
         this.$router.push(path)
       }
+    },
+    openPlus() {
+      this.$message.info('功能开发中')
+    },
+    /**
+     * 开启搜索:进入搜索模式并让输入框自动聚焦
+     */
+    openSearch() {
+      this.isSearching = true
+      // 使用 $nextTick 确保 DOM 渲染完成后再聚焦输入框
+      this.$nextTick(() => {
+        if (this.$refs.searchInput) {
+          this.$refs.searchInput.focus()
+        }
+      })
+    },
+
+    /**
+     * 关闭搜索:清空数据并退回常规标题栏
+     */
+    closeSearch() {
+      this.isSearching = false
+      this.searchQuery = ''
+      this.triggerSearch('') // 清空搜索条件,让子组件列表恢复完整数据
+    },
+
+    /**
+     * 监听输入框变化:加入标准防抖(Debounce),避免每打一个字就疯狂请求后端
+     */
+    handleSearchInput(val) {
+      if (this.searchTimer) {
+        clearTimeout(this.searchTimer)
+      }
+      this.searchTimer = setTimeout(() => {
+        this.triggerSearch(val.trim())
+      }, 300) // 延迟 300ms 触发真实业务搜索
+    },
+
+    /**
+     * 执行实际搜索业务
+     */
+    triggerSearch(keyword) {
+      const queryParams = {}
+      queryParams.keyword = keyword
+      searchChatRecords(queryParams).then(resp => {
+        if (resp.code === 0) {
+          // 将后端返回的数组赋值给响应式变量,前端 HTML 模板会自动刷新渲染
+          this.contactResult = resp.data.contactResult || []
+          this.messageResult = resp.data.messageResult || []
+        } else {
+          this.$message.error(resp.msg || '搜索失败')
+        }
+      }).catch(err => {
+        console.error('搜索接口异常:', err)
+      })
+      // 💡 联动核心:由于搜索框在父组件 Chat.vue,而列表在子路由组件(如 ChatList.vue 或 ChatContact.vue)中。
+      // 最直接、干净的跨组件传递方式是通过事件总线 (EventBus) 或直接利用 Vuex。
+      // 如果没有配置 Vuex,可以直接使用 Vue 原生的自定义事件派发:
+      // this.$root.$emit('global-search', keyword)
+    },
+    /**
+     * 关键词高亮函数
+     */
+    highlightText(text, keyword) {
+      if (!keyword) return text
+      const reg = new RegExp(`(${keyword})`, 'gi')
+      return text.replace(reg, '<span class="highlight">$1</span>')
+    },
+    /**
+     * 点击搜索结果跳转到对应聊天窗口
+     */
+    goToDialogue(item) {
+      this.closeSearch()
+      this.$router.push({
+        path: '/chat/dialogue',
+        query: {
+          chatId: item.chatId,
+          lastMessageId: item.messageId
+        }
+      })
     }
   }
 }
@@ -198,4 +352,142 @@ export default {
   height: 16px;
   line-height: 16px;
 }
+
+/* ==================== 搜索模式栏专属样式 ==================== */
+.search-bar-wrapper {
+  display: flex;
+  align-items: center;
+  width: 100%;
+  gap: 12px;
+}
+
+/* 覆写 Element UI 的输入框样式,使其更贴合微信的扁平化风格 */
+.wechat-search-input {
+  flex: 1;
+}
+
+.wechat-search-input ::v-deep .el-input__inner {
+  background-color: #ffffff;
+  border: none;
+  border-radius: 6px;
+  color: #191919;
+  font-size: 14px;
+}
+
+/* 微信风格的取消按钮 */
+.cancel-btn {
+  font-size: 15px;
+  color: #576b95; /* 微信专用深蓝色链接字,也可改成 #07c160 微信绿 */
+  cursor: pointer;
+  white-space: nowrap;
+  user-select: none;
+}
+.cancel-btn:active {
+  opacity: 0.7;
+}
+
+/* ==================== 搜索结果面板独立框架 ==================== */
+.search-result-panel {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: #f7f7f7; /* 微信底色 */
+  z-index: 99;              /* 确保盖在 router-view 的列表上面 */
+  overflow-y: auto;
+  -webkit-overflow-scrolling: touch;
+}
+
+/* 分组标题(如:联系人、聊天记录) */
+.result-group {
+  margin-top: 8px;
+  background-color: #fff;
+}
+.result-group .group-title {
+  font-size: 12px;
+  color: #b2b2b2;
+  padding: 16px 16px 4px 16px;
+  border-bottom: 1px solid #f0f0f0;
+}
+
+/* 单个结果行 */
+.result-item {
+  display: flex;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #f5f5f5;
+  cursor: pointer;
+  transition: background-color 0.1s;
+}
+.result-item:active {
+  background-color: #e5e5e5;
+}
+
+/* 头像 */
+.result-avatar {
+  width: 40px;
+  height: 40px;
+  border-radius: 4px;
+  margin-right: 12px;
+  flex-shrink: 0;
+}
+
+/* 右侧复合文本区 */
+.result-info {
+  flex: 1;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+}
+
+.result-meta {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.result-name {
+  font-size: 15px;
+  color: #191919;
+  font-weight: 500;
+}
+
+.result-time {
+  font-size: 11px;
+  color: #b2b2b2;
+}
+
+/* 聊天历史摘要描述 */
+.result-text {
+  font-size: 13px;
+  color: #666;
+  margin-top: 4px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+/* ==== 核心:微信高亮绿色字 ==== */
+::v-deep .highlight {
+  color: #07c160;
+  font-weight: bold;
+}
+
+/* 搜索无结果空状态 */
+.no-result {
+  text-align: center;
+  color: #b2b2b2;
+  font-size: 14px;
+  margin-top: 60px;
+}
+
+/* 浮层展开淡入淡出动画 */
+.fade-enter-active, .fade-leave-active {
+  transition: opacity 0.2s ease;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+}
 </style>

+ 18 - 30
src/views/chat/ChatAddressBook.vue

@@ -2,7 +2,12 @@
   <div class="address-book-wrapper">
     <div v-if="viewMode === 'list'" class="address-book-container">
       <div class="fixed-tools">
-        <div v-for="tool in tools" :key="tool.name" class="tool-item">
+        <div
+          v-for="tool in tools"
+          :key="tool.name"
+          class="tool-item"
+          @click="handleToolClick(tool)"
+        >
           <div class="icon-wrapper" :style="{ backgroundColor: tool.bgColor }">
             <i :class="tool.icon" />
           </div>
@@ -29,7 +34,7 @@
         <div class="back-btn" @click="backToList">
           <i class="el-icon-arrow-left" />
         </div>
-        <i class="el-icon-more right-icon" />
+        <i class="el-icon-more right-icon" @click="handleMore" />
       </header>
 
       <div class="user-detail-card">
@@ -37,7 +42,7 @@
           <div class="info-left">
             <h2 class="user-nickname">{{ selectedUser.screenName }}</h2>
             <p class="wechat-id">微信号:{{ selectedUser.username }}</p>
-            <p class="region-text">地区:广东 深圳</p>
+            <p class="region-text">地区:{{ area }}</p>
           </div>
           <el-avatar :size="64" shape="square" :src="selectedUser.avatarUrl" class="profile-avatar" />
         </div>
@@ -85,37 +90,14 @@ export default {
       // 核心控制变量:'list' 代表通讯录列表,'detail' 代表全屏用户详情
       viewMode: 'list',
       selectedUser: null,
-
+      area: '四川 成都',
       tools: [
         { name: '新的朋友', icon: 'el-icon-user-solid', bgColor: '#fa9d3b' },
         { name: '群聊', icon: 'el-icon-phone', bgColor: '#46c11b' },
         { name: '标签', icon: 'el-icon-collection-tag', bgColor: '#3cc51f' },
         { name: '公众号', icon: 'el-icon-s-promotion', bgColor: '#0bb20c' }
       ],
-      contacts: [
-        {
-          letter: 'C',
-          list: [
-            {
-              userId: 10007,
-              screenName: '陈七',
-              username: 'chenqi_99',
-              avatarUrl: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'
-            }
-          ]
-        },
-        {
-          letter: 'L',
-          list: [
-            { userId: 10002, screenName: '李四', username: 'lisi_004', avatarUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' },
-            { userId: 10008, screenName: '刘八', username: 'liuba_888', avatarUrl: 'https://cube.elemecdn.com/9/cbb/57175861119de33d35e44b5fed93bpng.png' }
-          ]
-        },
-        {
-          letter: 'Z',
-          list: [{ userId: 10003, screenName: '张三', username: 'zhangsan_33', avatarUrl: 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png' }]
-        }
-      ]
+      contacts: []
     }
   },
   created() {
@@ -129,6 +111,9 @@ export default {
         }
       })
     },
+    handleToolClick() {
+      this.$message.info('功能开发中')
+    },
     /**
      * 点击联系人:保存当前用户并原地切换到详情全屏视图
      */
@@ -137,6 +122,10 @@ export default {
       this.viewMode = 'detail'
     },
 
+    handleMore() {
+      this.$message.info('功能开发中')
+    },
+
     /**
      * 点击左上角返回:切回列表视图
      */
@@ -152,8 +141,7 @@ export default {
       this.$router.push({
         path: '/chat/dialogue',
         query: {
-          chatId: this.selectedUser.chatId,
-          receiverId: this.selectedUser.userId
+          chatId: this.selectedUser.chatId
         }
       })
       /* this.viewMode = 'list'

+ 124 - 15
src/views/chat/ChatDialogue.vue

@@ -5,7 +5,7 @@
         <i class="el-icon-arrow-left" />
       </div>
       <span class="chat-title">{{ chatReceiver.screenName }}</span>
-      <i class="el-icon-more right-icon" />
+      <i class="el-icon-more right-icon" @click="handleMore" />
     </header>
 
     <div ref="messageBox" class="message-container" @scroll="handleScroll">
@@ -85,6 +85,40 @@
               </div>
             </div>
 
+            <div v-else-if="msg.msgType === 6">
+              <div>语音通话 {{ msg.discardReason }} - {{ msg.duration }}</div>
+            </div>
+            <div v-else-if="msg.msgType === 7">
+              <div>视频通话 {{ msg.discardReason }} - {{ msg.duration }}</div>
+            </div>
+            <div
+              v-else-if="msg.msgType === 6"
+              :class="['msg-call-bubble', isMe(msg.sender.userId) ? 'is-self' : 'is-other']"
+            >
+              <i v-if="!isMe(msg.sender.userId)" class="el-icon-phone-outline call-icon" />
+
+              <div class="call-content">
+                <span class="call-text">{{ getCallStatusText(msg) }}</span>
+                <span v-if="msg.duration" class="call-duration">{{ msg.duration }}</span>
+              </div>
+
+              <i v-if="isMe(msg.sender.userId)" class="el-icon-phone-outline call-icon" />
+            </div>
+
+            <div
+              v-else-if="msg.msgType === 7"
+              :class="['msg-call-bubble', isMe(msg.sender.userId) ? 'is-self' : 'is-other']"
+            >
+              <i v-if="!isMe(msg.sender.userId)" class="el-icon-video-camera call-icon" />
+
+              <div class="call-content">
+                <span class="call-text">{{ getCallStatusText(msg) }}</span>
+                <span v-if="msg.duration" class="call-duration">{{ msg.duration }}</span>
+              </div>
+
+              <i v-if="isMe(msg.sender.userId)" class="el-icon-video-camera call-icon" />
+            </div>
+
             <div
               v-else-if="msg.msgType === 2"
               :class="['msg-transfer', { 'is-received': msg.transferStatus === 2 }]"
@@ -145,9 +179,9 @@
 
     <footer class="input-container">
       <div class="tool-bar">
-        <i class="el-icon-microphone" />
+        <i class="el-icon-microphone" @click="handleMore" />
         <el-input v-model="inputText" type="text" size="small" placeholder="发送消息..." @keyup.enter.native="sendMessage" />
-        <i class="el-icon-aim" />
+        <i class="el-icon-aim" @click="handleMore" />
         <el-button type="success" size="mini" class="send-btn" @click="sendMessage">发送</el-button>
       </div>
     </footer>
@@ -184,7 +218,7 @@
 </template>
 
 <script>
-import { getChatDialogue, getChatRecords, updateChatDialogue, getChatUser } from '@/api/chat'
+import { getChatDialogue, getChatRecords, updateChatDialogue } from '@/api/chat'
 import { getAuthedUser } from '@/utils/auth'
 
 export default {
@@ -256,6 +290,9 @@ export default {
     this.parseRouteParams()
   },
   methods: {
+    handleMore() {
+      this.$message.info('功能开发中')
+    },
     /**
      * 点击转账气泡
      */
@@ -413,7 +450,7 @@ export default {
       const query = this.$route.query
 
       // 1. 防御性拦截:如果关键参数缺失,进行报错提示或重定向
-      if (!query.chatId || !query.receiverId) {
+      if (!query.chatId) {
         this.$message.error('会话参数缺失,正在返回列表...')
         setTimeout(() => {
           this.goBack()
@@ -425,21 +462,14 @@ export default {
       // 注意:如果你的分布式雪花 ID 超过了 16 位(大于 9007199254740991),
       // 请不要转成 Number,保持字符串进行传输,否则 JS 会丢失后三位精度!
       this.currentContact.chatId = Number(query.chatId)
-      this.currentContact.receiverId = Number(query.receiverId)
-
-      getChatUser({ 'userId': this.currentContact.receiverId }).then(resp => {
-        if (resp.code === 0) {
-          this.chatReceiver = resp.data
-        }
-      })
-
       // 3. 联动逻辑:如果路由中没有传 lastMessageId,则强制补 0
       // this.lastReadMessageId = query.lastMessageId ? Number(query.lastMessageId) : 0
-      this.lastReadMessageId = 0
+      // this.lastReadMessageId = 0
       const queryParams = {}
       queryParams.chatId = this.currentContact.chatId
       getChatDialogue(queryParams).then(resp => {
         if (resp.code === 0) {
+          this.chatReceiver = resp.data.receiver
           this.lastReadMessageId = resp.data.lastMessageId
         }
       }).finally(() => {
@@ -737,6 +767,34 @@ export default {
         },
         images: [url]
       })
+    },
+
+    /**
+     * 根据拒绝原因和发送方,动态翻译成微信原生通话文案
+     * msg.discardReason 可选值示例:
+     * 'SUCCESS'(通话成功), 'CANCEL'(主动取消), 'REJECT'(对方拒绝), 'TIMEOUT'(无人接听)
+     */
+    getCallStatusText(msg) {
+      const isSelf = this.isMe(msg.sender.userId) // 是否是当前登录用户发出的
+      const reason = msg.discardReason
+
+      if (reason === 'SUCCESS' || reason === 'HANGUP') {
+        return '通话时长 '
+      }
+
+      if (reason === 'CANCEL') {
+        return isSelf ? '已取消' : '对方已取消'
+      }
+
+      if (reason === 'REJECT') {
+        return isSelf ? '对方已拒绝' : '已拒绝'
+      }
+
+      if (reason === 'TIMEOUT' || reason === 'MISSED') {
+        return isSelf ? '对方无应答' : '未接听'
+      }
+
+      return '通话结束'
     }
   }
 }
@@ -770,7 +828,7 @@ export default {
 }
 .left-action { cursor: pointer; display: flex; align-items: center; font-size: 16px; }
 .chat-title { font-size: 16px; font-weight: bold; }
-.right-icon { font-size: 18px; }
+.right-icon { font-size: 18px; cursor: pointer; }
 
 /* 消息流区域:高度自适应并允许滚动 */
 .message-container {
@@ -1155,4 +1213,55 @@ export default {
 .msg-transfer:not(.is-received) .transfer-icon-box i {
   transform: rotate(45deg); /* 待收钱时箭头倾斜 */
 }
+
+/* ==================== 音视频通话气泡基础样式 ==================== */
+.msg-call-bubble {
+  display: inline-flex;
+  align-items: center;
+  padding: 10px 14px;
+  border-radius: 4px;
+  font-size: 14px;
+  color: #191919;
+  max-width: 240px;
+  box-shadow: 0 1px 2px rgba(0,0,0,0.03);
+  word-break: break-all;
+}
+
+/* 自己发出的气泡(居右,文字靠右,通常背景已经是微信绿,通话气泡在微信里依然是白色/或跟常规气泡一致) */
+/* 注意:如果你的聊天界面整体“自己发的气泡”有统一绿色背景,通话消息可以继承或单独写 */
+.msg-call-bubble.is-self {
+  background-color: #95ec69; /* 微信标志绿 */
+}
+
+/* 对方发出的气泡(居左,白色) */
+.msg-call-bubble.is-other {
+  background-color: #ffffff;
+}
+
+/* 图标样式微调 */
+.call-icon {
+  font-size: 18px;
+}
+.msg-call-bubble.is-self .call-icon {
+  margin-left: 10px; /* 自己发出的,图标在文字右侧,加左边距 */
+}
+.msg-call-bubble.is-other .call-icon {
+  margin-right: 10px; /* 对方发出的,图标在文字左侧,加右边距 */
+}
+
+/* 核心内容区 */
+.call-content {
+  display: flex;
+  align-items: center;
+}
+
+.call-text {
+  line-height: 1.4;
+}
+
+/* 时长数字单独空开一点间距 */
+.call-duration {
+  margin-left: 6px;
+  font-weight: 500;
+}
 </style>

+ 1 - 2
src/views/chat/ChatList.vue

@@ -49,8 +49,7 @@ export default {
       this.$router.push({
         path: '/chat/dialogue',
         query: {
-          chatId: item.chatId,
-          receiverId: item.receiver.userId
+          chatId: item.chatId
         }
       })
     }

+ 63 - 9
src/views/chat/ChatMe.vue

@@ -1,16 +1,16 @@
 <template>
   <div class="me-container">
-    <div class="profile-card">
-      <el-avatar :size="60" shape="square" src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png" />
+    <div class="profile-card" @click="goToProfileDetail">
+      <el-avatar :size="60" shape="square" :src="my.avatarUrl" />
       <div class="profile-info">
         <div class="name">我的微信</div>
-        <div class="wechat-id">微信号: ai_collaborator_2026</div>
+        <div class="wechat-id">微信号: {{ my.username }}</div>
       </div>
       <i class="el-icon-arrow-right arrow" />
     </div>
 
     <div class="menu-group">
-      <div class="menu-item">
+      <div class="menu-item" @click="handleMenuClick('service')">
         <i class="el-icon-wallet icon-service" />
         <span class="menu-title">服务</span>
         <i class="el-icon-arrow-right arrow" />
@@ -18,17 +18,17 @@
     </div>
 
     <div class="menu-group">
-      <div class="menu-item">
+      <div class="menu-item" @click="handleMenuClick('collect')">
         <i class="el-icon-star-off icon-collect" />
         <span class="menu-title">收藏</span>
         <i class="el-icon-arrow-right arrow" />
       </div>
-      <div class="menu-item">
+      <div class="menu-item" @click="handleMenuClick('album')">
         <i class="el-icon-picture-outline icon-album" />
         <span class="menu-title">朋友圈</span>
         <i class="el-icon-arrow-right arrow" />
       </div>
-      <div class="menu-item">
+      <div class="menu-item" @click="handleMenuClick('card')">
         <i class="el-icon-tickets icon-card" />
         <span class="menu-title">卡包</span>
         <i class="el-icon-arrow-right arrow" />
@@ -36,7 +36,7 @@
     </div>
 
     <div class="menu-group">
-      <div class="menu-item">
+      <div class="menu-item" @click="handleMenuClick('setting')">
         <i class="el-icon-setting icon-setting" />
         <span class="menu-title">设置</span>
         <i class="el-icon-arrow-right arrow" />
@@ -46,8 +46,57 @@
 </template>
 
 <script>
+import { getAuthedUser } from '@/utils/auth'
+
 export default {
-  name: 'ChatMe'
+  name: 'ChatMe',
+  data() {
+    return {
+      my: null
+    }
+  },
+  created() {
+    this.my = getAuthedUser()
+  },
+  methods: {
+    goToProfileDetail() {
+      this.$message.info('功能开发中')
+    },
+    /**
+     * 统一处理个人中心菜单点击
+     * @param {String} type 菜单类型
+     */
+    handleMenuClick(type) {
+      switch (type) {
+        case 'setting':
+          // 如果有设置页面,进行路由跳转
+          // this.$router.push('/chat/setting')
+          this.$message.info('功能开发中')
+          break
+
+        case 'album':
+          // 跳转到朋友圈/相册
+          // this.$router.push('/chat/album')
+          this.$message.info('功能开发中')
+          break
+
+        case 'service':
+          this.$message.info('功能开发中')
+          break
+
+        case 'collect':
+          this.$message.info('功能开发中')
+          break
+
+        case 'card':
+          this.$message.info('功能开发中')
+          break
+
+        default:
+          this.$message.warning('未知操作')
+      }
+    }
+  }
 }
 </script>
 
@@ -64,6 +113,11 @@ export default {
   padding: 24px 16px;
   margin-bottom: 8px;
   cursor: pointer;
+  transition: background-color 0.1s ease; /* 让变灰和恢复的过程有微小的平滑过渡 */
+  user-select: none;
+}
+.profile-card:active {
+  background-color: #e5e5e5; /* 微信经典的点击灰色背景色 */
 }
 .profile-info {
   flex: 1;