From 27cd042df9aca0383a49f3514bc21958dd890912 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期一, 29 六月 2026 15:42:23 +0800
Subject: [PATCH] 银川 1.联调产品维护页面 2.添加IM即时通讯模块

---
 src/views/im/home/store/uiStore.ts |  155 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 155 insertions(+), 0 deletions(-)

diff --git a/src/views/im/home/store/uiStore.ts b/src/views/im/home/store/uiStore.ts
new file mode 100644
index 0000000..93d0b6e
--- /dev/null
+++ b/src/views/im/home/store/uiStore.ts
@@ -0,0 +1,155 @@
+import type { GroupLite, User } from '../types'
+
+import { reactive, ref } from 'vue'
+
+import { acceptHMRUpdate, defineStore } from 'pinia'
+
+import { ImFriendAddSource } from '../../utils/constants'
+
+/**
+ * IM 鍏ㄥ眬 UI store
+ *
+ * 鏀剁撼鏍囧噯锛氳Е鍙戠偣 N 涓�佹寕杞界偣鎯充繚鎸� 1 涓殑娴眰鐘舵�併��
+ * 浠绘剰浣嶇疆閮藉彲鑳� open锛屼絾 DOM 涓婂彧鎯崇暀涓�浠藉疄渚� 鈫� 璧� store 娲惧彂锛�
+ * 鐢� `index.vue` 鎸備竴涓闃呯粍浠剁粺涓�娓叉煋銆�
+ */
+export const useImUiStore = defineStore('imUiStore', () => {
+  // ==================== 鐢ㄦ埛鍚嶇墖 UserInfoCard ====================
+  // 鐢ㄦ埛鍚嶇墖鎮诞鍗�
+
+  const userInfoCard = reactive({
+    show: false,
+    user: null as null | User,
+    position: { x: 0, y: 0 },
+    // addSource / addSourceExtra 璺熼殢瑙﹀彂鐐瑰甫鍏ャ�屽姞濂藉弸銆嶆潵婧愶紙缇ゆ垚鍛樺叆鍙� = GROUP + 缇ゅ悕锛涘叾浣欓粯璁ゆ悳绱級
+    addSource: ImFriendAddSource.SEARCH as number,
+    addSourceExtra: '' as string
+  })
+
+  /** 鎵撳紑鐢ㄦ埛鍚嶇墖 */
+  function openUserInfoCard(
+    user: User,
+    position: { x: number; y: number },
+    addSource: number = ImFriendAddSource.SEARCH,
+    addSourceExtra: string = ''
+  ) {
+    const viewportWidth = document.documentElement.clientWidth
+    const viewportHeight = document.documentElement.clientHeight
+    userInfoCard.user = user
+    userInfoCard.position.x = Math.min(position.x, viewportWidth - 350)
+    userInfoCard.position.y = Math.min(position.y, viewportHeight - 220)
+    userInfoCard.addSource = addSource
+    userInfoCard.addSourceExtra = addSourceExtra
+    userInfoCard.show = true
+  }
+
+  /** 榧犳爣鐐瑰嚮浣嶇疆 + 20px 妯悜鍋忕Щ鎵撳紑鍚嶇墖锛氶伩鍏嶅悕鐗囩洿鎺ヨ鐩栬Е鍙戝厓绱狅紝瀵归綈澶村儚 / 鍚嶇墖娑堟伅绛夌偣鍑讳氦浜掔殑缁熶竴瑙傛劅 */
+  function openUserInfoCardAtEvent(
+    user: User,
+    e: MouseEvent,
+    addSource: number = ImFriendAddSource.SEARCH,
+    addSourceExtra: string = ''
+  ) {
+    openUserInfoCard(user, { x: e.clientX + 20, y: e.clientY }, addSource, addSourceExtra)
+  }
+
+  /** 鍏抽棴鐢ㄦ埛鍚嶇墖 */
+  function closeUserInfoCard() {
+    userInfoCard.show = false
+  }
+
+  // ==================== 缇ゅ悕鐗� GroupInfoCard ====================
+
+  const groupInfoCard = reactive({
+    show: false,
+    group: null as GroupLite | null,
+    position: { x: 0, y: 0 }
+  })
+
+  /** 榧犳爣鐐瑰嚮浣嶇疆 + 20px 妯悜鍋忕Щ鎵撳紑缇ゅ悕鐗囷紝瀵归綈 UserInfoCard 鐨勭粺涓�瑙傛劅 */
+  function openGroupInfoCardAtEvent(group: GroupLite, e: MouseEvent) {
+    const viewportWidth = document.documentElement.clientWidth
+    const viewportHeight = document.documentElement.clientHeight
+    groupInfoCard.group = group
+    groupInfoCard.position.x = Math.min(e.clientX + 20, viewportWidth - 350)
+    groupInfoCard.position.y = Math.min(e.clientY, viewportHeight - 220)
+    groupInfoCard.show = true
+  }
+
+  /** 鍏抽棴缇ゅ悕鐗� */
+  function closeGroupInfoCard() {
+    groupInfoCard.show = false
+  }
+
+  // ==================== 鍙抽敭鑿滃崟 ContextMenu ====================
+  // 鍙抽敭鑿滃崟铏界劧鏄竴涓粍浠舵寕鍦ㄤ富澹充笂锛屼絾鍏惰Е鍙戞椂鏈哄垎鏁e湪鍚勫垪琛�
+  interface ContextMenuItem {
+    key: string
+    name: string
+    disabled?: boolean
+    divided?: boolean // 鏄惁鍦ㄨ椤逛笂鏂规樉绀哄垎鍓茬嚎锛堢敤浜庢妸"鍒犻櫎"绛夊嵄闄╂搷浣滀笌涓婇潰鐨勫父瑙勯」闅斿紑锛�
+    danger?: boolean // 鏄惁璧板嵄闄╂搷浣滄牱寮忥紙绾㈣壊鏂囧瓧锛�
+    icon?: string // 鍙�� iconify 鍥炬爣鍚嶏紙濡� ant-design:delete-outlined锛夛紱涓嶄紶鍒欎笉娓叉煋鍓嶇疆鍥炬爣
+  }
+
+  const contextMenu = reactive({
+    show: false,
+    position: { x: 0, y: 0 },
+    items: [] as ContextMenuItem[],
+    /** 閫変腑鍥炶皟锛氭瘡娆� open 鏃剁敱璋冪敤鏂逛紶鍏� */
+    onSelect: null as ((item: ContextMenuItem) => void) | null
+  })
+
+  /** 鎵撳紑鍙抽敭鑿滃崟 */
+  function openContextMenu(
+    position: { x: number; y: number },
+    items: ContextMenuItem[],
+    onSelect: (item: ContextMenuItem) => void
+  ) {
+    contextMenu.position = position
+    contextMenu.items = items
+    contextMenu.onSelect = onSelect
+    contextMenu.show = true
+  }
+
+  /** 鍏抽棴鍙抽敭鑿滃崟 */
+  function closeContextMenu() {
+    contextMenu.show = false
+    contextMenu.onSelect = null
+  }
+
+  // ==================== 娑堟伅 Tab 璺宠浆涓嬩竴鏈 ====================
+  // 鍦� ImHomeConversation 椤甸潰鍐嶆鐐瑰嚮宸ュ叿鏍忋�屾秷鎭�嶆椂瑙﹀彂锛�
+  // 閫氳繃閫掑 nonce 璁� conversation/index.vue 鐨� watch 鎰熺煡鍚庢墽琛屾粴鍔� + 楂樹寒
+
+  const nextUnreadJumpNonce = ref(0)
+
+  /** 璇锋眰婊氬姩鍒颁笅涓�涓湭璇讳細璇濓紙鍚厤鎵撴壈锛� */
+  function requestNextUnreadJump() {
+    nextUnreadJumpNonce.value++
+  }
+
+  return {
+    userInfoCard,
+    openUserInfoCard,
+    openUserInfoCardAtEvent,
+    closeUserInfoCard,
+
+    groupInfoCard,
+    openGroupInfoCardAtEvent,
+    closeGroupInfoCard,
+
+    contextMenu,
+    openContextMenu,
+    closeContextMenu,
+
+    nextUnreadJumpNonce,
+    requestNextUnreadJump
+  }
+})
+
+// dev: 璁� Pinia 鐨� actions / state 鏀瑰姩鏀寔 HMR锛岄伩鍏嶆瘡娆℃敼 store 閮藉緱纭埛
+// 鍚﹀垯 Vite 鎶婃柊妯″潡鎺ㄤ笅鏉ュ悗锛岃�� store 瀹炰緥鐨� action 闂寘浠嶆寚鍚戞棫鍑芥暟浣�
+if (import.meta.hot) {
+  import.meta.hot.accept(acceptHMRUpdate(useImUiStore, import.meta.hot))
+}

--
Gitblit v1.9.3