admin-vue3/src/views/im/home/store/uiStore.ts

113 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { defineStore, acceptHMRUpdate } from 'pinia'
import { reactive } from 'vue'
import { ImFriendAddSource } from '../../utils/constants'
import type { User } from '../types'
/**
* IM 全局 UI store
*
* 收纳标准:触发点 N 个、挂载点想保持 1 个的浮层状态。
* 任意位置都可能 open但 DOM 上只想留一份实例 → 走 store 派发,
* 由 `Index.vue` 挂一个订阅组件统一渲染。
*/
export const useImUiStore = defineStore('imUiStore', () => {
// ==================== 用户名片 UserInfoCard ====================
// 用户名片悬浮卡
const userInfoCard = reactive({
show: false,
user: null as User | null,
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
}
// ==================== 右键菜单 ContextMenu ====================
// 右键菜单虽然是一个组件挂在主壳上,但其触发时机分散在各列表
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
}
return {
userInfoCard,
openUserInfoCard,
openUserInfoCardAtEvent,
closeUserInfoCard,
contextMenu,
openContextMenu,
closeContextMenu
}
})
// dev: 让 Pinia 的 actions / state 改动支持 HMR避免每次改 store 都得硬刷
// 否则 Vite 把新模块推下来后,老 store 实例的 action 闭包仍指向旧函数体
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useImUiStore, import.meta.hot))
}