mirror of
https://github.com/HuLaSpark/HuLa.git
synced 2026-09-24 23:11:59 +08:00
perf(component): ⚡ 优化右键菜单功能
新增添加好友功能
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
],
|
||||
"definitions": {
|
||||
"Capability": {
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```",
|
||||
"type": "object",
|
||||
"required": ["identifier", "permissions"],
|
||||
"properties": {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="globalStore.addFriendModalInfo.show"
|
||||
:mask-closable="false"
|
||||
class="rounded-8px"
|
||||
transform-origin="center">
|
||||
<div class="bg-[--bg-edit] w-380px h-fit box-border flex flex-col">
|
||||
<n-flex :size="6" vertical>
|
||||
<div
|
||||
v-if="type() === 'macos'"
|
||||
@click="close"
|
||||
class="mac-close size-13px shadow-inner bg-#ed6a5eff rounded-50% mt-6px select-none absolute left-6px">
|
||||
<svg class="hidden size-7px color-#000 font-bold select-none absolute top-3px left-3px">
|
||||
<use href="#close"></use>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<n-flex class="text-(14px --text-color) select-none pt-6px" justify="center">申请加好友</n-flex>
|
||||
|
||||
<svg
|
||||
v-if="type() === 'windows'"
|
||||
class="size-14px cursor-pointer pt-6px select-none absolute right-6px"
|
||||
@click="close">
|
||||
<use href="#close"></use>
|
||||
</svg>
|
||||
<span class="h-1px w-full bg-[--line-color]"></span>
|
||||
</n-flex>
|
||||
|
||||
<n-flex vertical justify="center" :size="20" class="p-20px">
|
||||
<n-flex align="center" justify="center" :size="20">
|
||||
<n-avatar round size="large" :src="userInfo.avatar" />
|
||||
<n-flex vertical :size="10">
|
||||
<p class="text-[--text-color]">{{ userInfo.name }}</p>
|
||||
<p class="text-(12px [--text-color])">uid: {{ userInfo.uid }}</p>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
|
||||
<n-input
|
||||
v-model:value="requestMsg"
|
||||
:allow-input="(value: string) => !value.startsWith(' ') && !value.endsWith(' ')"
|
||||
:autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 3
|
||||
}"
|
||||
:maxlength="60"
|
||||
:count-graphemes="countGraphemes"
|
||||
show-count
|
||||
type="textarea"
|
||||
placeholder="输入几句话,对TA说些什么吧" />
|
||||
|
||||
<n-button color="#13987f" @click="addFriend">添加好友</n-button>
|
||||
</n-flex>
|
||||
</div>
|
||||
</n-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useGlobalStore } from '@/stores/global.ts'
|
||||
import { type } from '@tauri-apps/plugin-os'
|
||||
import { useUserInfo } from '@/hooks/useCached.ts'
|
||||
import { leftHook } from '@/layout/left/hook.ts'
|
||||
import apis from '@/services/apis.ts'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const { countGraphemes } = leftHook()
|
||||
const userInfo = ref(useUserInfo(globalStore.addFriendModalInfo.uid).value)
|
||||
const requestMsg = ref()
|
||||
|
||||
const close = () => {
|
||||
globalStore.addFriendModalInfo.show = false
|
||||
globalStore.addFriendModalInfo.uid = undefined
|
||||
}
|
||||
|
||||
const addFriend = async () => {
|
||||
await apis.sendAddFriendRequest({
|
||||
msg: requestMsg.value,
|
||||
targetUid: globalStore.addFriendModalInfo.uid as number
|
||||
})
|
||||
window.$message.success('已发送好友申请')
|
||||
close()
|
||||
}
|
||||
|
||||
watch(globalStore.addFriendModalInfo, (val) => {
|
||||
if (val.show) {
|
||||
userInfo.value = useUserInfo(val.uid).value
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -36,8 +36,8 @@
|
||||
left: `${pos.posX}px`,
|
||||
top: `${pos.posY}px`
|
||||
}">
|
||||
<div v-resize="handleSize" v-if="menu && menu.length > 0" class="menu-list">
|
||||
<div v-for="(item, index) in menu as any[]" :key="index">
|
||||
<div v-resize="handleSize" v-if="visibleMenu && visibleMenu.length > 0" class="menu-list">
|
||||
<div v-for="(item, index) in visibleMenu as any[]" :key="index">
|
||||
<!-- 禁止的菜单选项需要禁止点击事件 -->
|
||||
<div class="menu-item-disabled" v-if="item.disabled" @click.prevent="$event.preventDefault()">
|
||||
<svg><use :href="`#${item.icon}`"></use></svg>
|
||||
@@ -68,7 +68,11 @@
|
||||
import { useContextMenu } from '@/hooks/useContextMenu.ts'
|
||||
import { useViewport } from '@/hooks/useViewport.ts'
|
||||
|
||||
const { menu, emoji, specialMenu } = defineProps({
|
||||
const { content, menu, emoji, specialMenu } = defineProps({
|
||||
content: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
menu: {
|
||||
type: Array
|
||||
},
|
||||
@@ -80,6 +84,17 @@ const { menu, emoji, specialMenu } = defineProps({
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
// 使用计算属性过滤显示的菜单项
|
||||
const visibleMenu = computed(() => {
|
||||
return menu?.filter((item: any) => {
|
||||
// 检查是否有 visible 属性并作为函数调用
|
||||
if (typeof item.visible === 'function') {
|
||||
return item.visible(content) // 如果 visible 是函数,则调用它
|
||||
}
|
||||
// 如果没有 visible 属性,则默认显示
|
||||
return true
|
||||
})
|
||||
})
|
||||
/** 判断是否传入了menu */
|
||||
const isNull = computed(() => menu === void 0)
|
||||
const containerRef = ref(null)
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<n-flex vertical class="select-none">
|
||||
<n-flex align="center" justify="space-between" class="color-[--text-color] px-20px py-10px">
|
||||
<p class="text-16px">好友通知</p>
|
||||
<svg class="size-18px cursor-pointer"><use href="#delete"></use></svg>
|
||||
</n-flex>
|
||||
|
||||
<n-flex
|
||||
vertical
|
||||
:size="10"
|
||||
class="p-[0_30px]"
|
||||
v-for="(item, index) in contactStore.requestFriendsList"
|
||||
:key="index">
|
||||
<n-flex align="center" justify="space-between" class="bg-[--center-bg-color] rounded-10px p-20px box-border">
|
||||
<n-flex align="center" :size="10">
|
||||
<n-avatar round size="large" :src="useUserInfo(item.uid).value.avatar" class="mr-10px" />
|
||||
<n-flex vertical :size="12">
|
||||
<n-flex align="center" :size="10">
|
||||
<n-popover
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
:show-arrow="false"
|
||||
style="padding: 0; background: var(--bg-info); backdrop-filter: blur(10px)">
|
||||
<template #trigger>
|
||||
<p @click="currentUserId = item.uid" class="text-(14px #13987f) cursor-pointer">
|
||||
{{ useUserInfo(item.uid).value.name }}
|
||||
</p>
|
||||
</template>
|
||||
<!-- 用户个人信息框 -->
|
||||
<InfoPopover v-if="currentUserId === item.uid" :uid="item.uid" />
|
||||
</n-popover>
|
||||
|
||||
<p class="text-(14px [--text-color])">请求加为好友</p>
|
||||
</n-flex>
|
||||
<p v-show="item.msg" class="text-(12px [--text-color])">留言:{{ item.msg }}</p>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
|
||||
<n-button
|
||||
secondary
|
||||
v-if="item.status === RequestFriendAgreeStatus.Waiting"
|
||||
@click="contactStore.onAcceptFriend(item.applyId)">
|
||||
接受
|
||||
</n-button>
|
||||
<span class="text-(12px [--text-color])" v-else>已添加</span>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</n-flex>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { useContactStore } from '@/stores/contacts.ts'
|
||||
import { useUserInfo } from '@/hooks/useCached.ts'
|
||||
import { RequestFriendAgreeStatus } from '@/services/types.ts'
|
||||
|
||||
const contactStore = useContactStore()
|
||||
const currentUserId = ref(0)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -124,7 +124,7 @@ const { content } = defineProps<{
|
||||
content: any
|
||||
}>()
|
||||
const item = computed(() => {
|
||||
return useUserInfo(content.value.uid).value
|
||||
return useUserInfo(content.uid).value
|
||||
})
|
||||
|
||||
const footerOptions = ref<OPT.Details[]>([
|
||||
|
||||
@@ -91,6 +91,7 @@
|
||||
<template #trigger>
|
||||
<ContextMenu
|
||||
@select="$event.click(item, 'Main')"
|
||||
:content="item"
|
||||
:menu="chatStore.isGroup ? optionsList : void 0"
|
||||
:special-menu="report">
|
||||
<n-avatar
|
||||
@@ -122,6 +123,7 @@
|
||||
:style="item.fromUser.uid === userUid ? 'flex-direction: row-reverse' : ''">
|
||||
<ContextMenu
|
||||
@select="$event.click(item)"
|
||||
:content="item"
|
||||
:menu="chatStore.isGroup ? optionsList : []"
|
||||
:special-menu="report">
|
||||
<n-flex
|
||||
@@ -173,6 +175,7 @@
|
||||
</n-flex>
|
||||
<!-- 气泡样式 -->
|
||||
<ContextMenu
|
||||
:content="item"
|
||||
@contextmenu="handleMacSelect"
|
||||
@mouseenter="handleMouseEnter(item.message.id)"
|
||||
@mouseleave="handleMouseLeave"
|
||||
@@ -575,9 +578,9 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
Mitt.on(`${MittEnum.INFO_POPOVER}-Main`, (event: any) => {
|
||||
selectKey.value = event
|
||||
selectKey.value = event.uid
|
||||
infoPopover.value = true
|
||||
handlePopoverUpdate(event)
|
||||
handlePopoverUpdate(event.uid)
|
||||
})
|
||||
Mitt.on(MittEnum.MSG_BOX_SHOW, (event: any) => {
|
||||
activeItemRef.value = event.item
|
||||
|
||||
@@ -57,7 +57,11 @@
|
||||
v-model:show="infoPopover"
|
||||
style="padding: 0; background: var(--bg-info); backdrop-filter: blur(10px)">
|
||||
<template #trigger>
|
||||
<ContextMenu @select="$event.click(item, 'Sidebar')" :menu="optionsList" :special-menu="report">
|
||||
<ContextMenu
|
||||
:content="item"
|
||||
@select="$event.click(item, 'Sidebar')"
|
||||
:menu="optionsList"
|
||||
:special-menu="report">
|
||||
<n-flex @click="selectKey = item.uid" :key="item.uid" :size="10" align="center" class="item">
|
||||
<n-avatar
|
||||
lazy
|
||||
@@ -133,9 +137,9 @@ const handleSearch = () => {
|
||||
|
||||
onMounted(() => {
|
||||
Mitt.on(`${MittEnum.INFO_POPOVER}-Sidebar`, (event: any) => {
|
||||
selectKey.value = event
|
||||
selectKey.value = event.uid
|
||||
infoPopover.value = true
|
||||
handlePopoverUpdate(event)
|
||||
handlePopoverUpdate(event.uid)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -62,6 +62,8 @@ export enum MittEnum {
|
||||
SHRINK_WINDOW,
|
||||
/** 详情页面显示 */
|
||||
DETAILS_SHOW,
|
||||
/** 好友申请页面显示 */
|
||||
APPLY_SHOW,
|
||||
/** 消息列表被清空或者暂无消息 */
|
||||
NOT_MSG,
|
||||
/** 回复消息 */
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { useCommon } from '@/hooks/useCommon.ts'
|
||||
import { MittEnum, MsgEnum } from '@/enums'
|
||||
import { MittEnum, MsgEnum, PowerEnum } from '@/enums'
|
||||
import { MessageType, SessionItem } from '@/services/types.ts'
|
||||
import Mitt from '@/utils/Bus.ts'
|
||||
import { useChatStore } from '@/stores/chat.ts'
|
||||
import apis from '@/services/apis.ts'
|
||||
import { useContactStore } from '@/stores/contacts'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useGlobalStore } from '@/stores/global.ts'
|
||||
|
||||
export const useChatMain = (activeItem?: SessionItem) => {
|
||||
const { removeTag, userUid } = useCommon()
|
||||
const globalStore = useGlobalStore()
|
||||
const chatStore = useChatStore()
|
||||
const userInfo = useUserStore()?.userInfo
|
||||
// const userInfo = useUserStore()?.userInfo
|
||||
// const chatMessageList = computed(() => chatStore.chatMessageList)
|
||||
const messageOptions = computed(() => chatStore.currentMessageOptions)
|
||||
@@ -80,6 +85,11 @@ export const useChatMain = (activeItem?: SessionItem) => {
|
||||
return
|
||||
}
|
||||
chatStore.updateRecallStatus({ msgId: item.message.id })
|
||||
},
|
||||
visible: (item: MessageType) => {
|
||||
const isCurrentUser = item.fromUser.uid === userUid.value
|
||||
const isAdmin = userInfo?.power === PowerEnum.ADMIN
|
||||
return isCurrentUser || isAdmin
|
||||
}
|
||||
}
|
||||
])
|
||||
@@ -153,7 +163,7 @@ export const useChatMain = (activeItem?: SessionItem) => {
|
||||
}
|
||||
])
|
||||
/** 右键用户信息菜单(群聊的时候显示) */
|
||||
const optionsList = ref([
|
||||
const optionsList = ref<OPT.RightMenu[]>([
|
||||
{
|
||||
label: '发送信息',
|
||||
icon: 'message-action',
|
||||
@@ -170,13 +180,19 @@ export const useChatMain = (activeItem?: SessionItem) => {
|
||||
label: '查看资料',
|
||||
icon: 'notes',
|
||||
click: (item: any, type: string) => {
|
||||
Mitt.emit(`${MittEnum.INFO_POPOVER}-${type}`, item.key)
|
||||
// 如果是聊天框内的资料就使用的是消息的key,如果是群聊成员的资料就使用的是uid
|
||||
const uid = item.uid || item.message.id
|
||||
Mitt.emit(`${MittEnum.INFO_POPOVER}-${type}`, { uid: uid, type: type })
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '添加好友',
|
||||
icon: 'people-plus',
|
||||
click: () => {}
|
||||
click: (item: any) => {
|
||||
globalStore.addFriendModalInfo.show = true
|
||||
globalStore.addFriendModalInfo.uid = item.uid || item.fromUser.uid
|
||||
},
|
||||
visible: (item: any) => canAddFriend(item.uid || item.fromUser.uid)
|
||||
}
|
||||
])
|
||||
/** 举报选项 */
|
||||
@@ -207,6 +223,19 @@ export const useChatMain = (activeItem?: SessionItem) => {
|
||||
}
|
||||
])
|
||||
|
||||
/**
|
||||
* 判断用户是否可以添加好友
|
||||
* @param uid 用户 ID
|
||||
* @returns {boolean} 如果可以添加好友返回 true,否则返回 false
|
||||
*/
|
||||
const canAddFriend = (uid: number): boolean => {
|
||||
const contactStore = useContactStore()
|
||||
const userStore = useUserStore()
|
||||
const myUid = userStore.userInfo.uid
|
||||
// 好友和自己不显示添加好友菜单
|
||||
return !(contactStore.contactsList.some((item) => item.uid === uid) || uid === myUid)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理复制事件
|
||||
* @param content 复制的内容
|
||||
|
||||
+15
-1
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<div class="flex size-full min-w-310px">
|
||||
<div id="layout" class="flex size-full min-w-310px">
|
||||
<Left />
|
||||
<Center />
|
||||
<Right v-if="!shrinkStatus" />
|
||||
</div>
|
||||
|
||||
<AddFriendsModal />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -13,7 +15,13 @@ import Right from './right/index.vue'
|
||||
import Mitt from '@/utils/Bus'
|
||||
import { MittEnum } from '@/enums'
|
||||
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
|
||||
import { useGlobalStore } from '@/stores/global.ts'
|
||||
import { useContactStore } from '@/stores/contacts.ts'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const contactStore = useContactStore()
|
||||
// 清空未读消息
|
||||
globalStore.unReadMark.newMsgUnreadCount = 0
|
||||
const shrinkStatus = ref(false)
|
||||
/**
|
||||
* event默认如果没有传递值就为true,所以shrinkStatus的值为false就会发生值的变化
|
||||
@@ -23,6 +31,12 @@ Mitt.on(MittEnum.SHRINK_WINDOW, (event) => {
|
||||
shrinkStatus.value = event as boolean
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
// 默认执行一次
|
||||
contactStore.getContactList(true)
|
||||
contactStore.getRequestFriendsList(true)
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
await getCurrentWebviewWindow().show()
|
||||
})
|
||||
|
||||
@@ -43,7 +43,11 @@
|
||||
</n-flex>
|
||||
</n-popover>
|
||||
<!-- 该选项无提示时展示 -->
|
||||
<n-badge v-else :max="99" :value="item.badge">
|
||||
<n-badge
|
||||
v-else
|
||||
:max="99"
|
||||
:value="unReadMark.newMsgUnreadCount"
|
||||
:show="item.icon.includes('message') && unReadMark.newMsgUnreadCount > 0">
|
||||
<svg class="size-22px">
|
||||
<use
|
||||
:href="`#${activeUrl === item.url || openWindowsList.has(item.url) ? item.iconAction : item.icon}`"></use>
|
||||
@@ -249,11 +253,14 @@ import { PluginEnum, ShowModeEnum } from '@/enums'
|
||||
import { useSettingStore } from '@/stores/setting.ts'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import { useGlobalStore } from '@/stores/global.ts'
|
||||
|
||||
const globalStore = useGlobalStore()
|
||||
const pluginsStore = usePluginsStore()
|
||||
const { showMode } = storeToRefs(useSettingStore())
|
||||
const { menuTop } = useMenuTopStore()
|
||||
const { plugins } = storeToRefs(pluginsStore)
|
||||
const unReadMark = computed(() => globalStore.unReadMark)
|
||||
// const headerRef = useTemplateRef('header')
|
||||
// const actionListRef = useTemplateRef('actionList')
|
||||
//const { } = toRefs(getCurrentInstance) // 所有菜单的外层div
|
||||
|
||||
@@ -120,11 +120,7 @@ export const leftHook = () => {
|
||||
window.$message.error('改名次数不足')
|
||||
return
|
||||
}
|
||||
apis.modifyUserName(editInfo.value.content.name).then((res: any) => {
|
||||
if (!res) {
|
||||
window.$message.error(res)
|
||||
return
|
||||
}
|
||||
apis.modifyUserName(editInfo.value.content.name).then(() => {
|
||||
// 更新本地缓存的用户信息
|
||||
login.accountInfo.name = editInfo.value.content.name!
|
||||
updateCurrentUserCache('name', editInfo.value.content.name) // 更新缓存里面的用户信息
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<!-- 导航选项按钮模块 -->
|
||||
<ActionList />
|
||||
<!-- 编辑资料弹窗 -->
|
||||
<InfoPopover />
|
||||
<InfoEdit />
|
||||
|
||||
<!-- 弹出框 -->
|
||||
<component :is="componentMap" />
|
||||
@@ -18,7 +18,7 @@
|
||||
<script lang="tsx" setup>
|
||||
import LeftAvatar from './components/LeftAvatar.vue'
|
||||
import ActionList from './components/ActionList.vue'
|
||||
import InfoPopover from './components/InfoPopover.vue'
|
||||
import InfoEdit from './components/InfoEdit.vue'
|
||||
import Mitt from '@/utils/Bus.ts'
|
||||
import { lock, LockScreen, CheckUpdate } from './model.tsx'
|
||||
import { DefineComponent, DefineSetupFnComponent } from 'vue'
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<main data-tauri-drag-region class="flex-1 bg-[--right-bg-color] h-full w-100vw min-w-600px">
|
||||
<div class="size-full" style="background: var(--right-theme-bg-color)" data-tauri-drag-region>
|
||||
<div class="size-full" :style="{ background: isChat ? 'var(--right-theme-bg-color)' : '' }" data-tauri-drag-region>
|
||||
<ActionBar :current-label="appWindow.label" />
|
||||
<!-- 需要判断当前路由是否是信息详情界面 -->
|
||||
<ChatBox :active-item="activeItem" v-if="msgBoxShow && isChat && activeItem !== -1" />
|
||||
|
||||
<Details :content="DetailsContent" v-else-if="detailsShow && isDetails" />
|
||||
|
||||
<!-- 好友申请列表 -->
|
||||
<ApplyList v-else-if="DetailsContent && DetailsContent.type === 'apply'" />
|
||||
|
||||
<!-- 聊天界面背景图标 -->
|
||||
<div v-else class="flex-center size-full select-none">
|
||||
<img
|
||||
@@ -73,7 +76,11 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
if (isDetails) {
|
||||
Mitt.on(MittEnum.APPLY_SHOW, (event: any) => {
|
||||
DetailsContent.value = event.context
|
||||
})
|
||||
Mitt.on(MittEnum.DETAILS_SHOW, (event: any) => {
|
||||
console.log('event', event)
|
||||
DetailsContent.value = event.context
|
||||
detailsShow.value = event.detailsShow as boolean
|
||||
})
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// import { createAxios } from '@/services/request'
|
||||
import urls from '@/services/urls'
|
||||
import type {
|
||||
Response,
|
||||
// BadgeType,
|
||||
BadgeType,
|
||||
CacheBadgeItem,
|
||||
CacheBadgeReq,
|
||||
CacheUserItem,
|
||||
@@ -57,7 +56,7 @@ export default {
|
||||
/** 获取用户详细信息 */
|
||||
getUserDetail: () => GET<UserInfoType>(urls.getUserInfoDetail, {}),
|
||||
/** 获取徽章列表 */
|
||||
getBadgeList: (): Promise<Response> => GET(urls.getBadgeList),
|
||||
getBadgeList: (): Promise<BadgeType> => GET(urls.getBadgeList),
|
||||
/** 设置用户勋章 */
|
||||
setUserBadge: (badgeId: number) => PUT<void>(urls.setUserBadge, { badgeId }),
|
||||
/** 修改用户名 */
|
||||
|
||||
+11
-3
@@ -1,6 +1,7 @@
|
||||
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'
|
||||
import { useSettingStore } from '@/stores/setting.ts'
|
||||
import Http, { HttpParams } from './http.ts'
|
||||
import { ServiceResponse } from '@/services/types.ts'
|
||||
|
||||
/** 是否是测试环境 */
|
||||
const isTest = computed(() => {
|
||||
@@ -179,7 +180,9 @@ const responseInterceptor = async <T>(
|
||||
const data = await Http(url, httpParams, true)
|
||||
|
||||
const resp = data.resp
|
||||
const serviceData = (await data.data) as ServiceResponse
|
||||
|
||||
//检查发送请求是否成功
|
||||
if (resp.status > 400) {
|
||||
let message = ''
|
||||
switch (resp.status) {
|
||||
@@ -225,9 +228,14 @@ const responseInterceptor = async <T>(
|
||||
}
|
||||
return Promise.reject(`err: ${message}, status: ${resp.status}`)
|
||||
}
|
||||
|
||||
const res: any = await data.data
|
||||
return Promise.resolve(res.data)
|
||||
console.log(data)
|
||||
//检查服务端返回是否成功,并且中断请求
|
||||
if (!serviceData.success) {
|
||||
window.$message.error(serviceData.errMsg)
|
||||
return Promise.reject(`http error: ${serviceData.errMsg}`)
|
||||
}
|
||||
console.log(data)
|
||||
return Promise.resolve(serviceData.data)
|
||||
} catch (err) {
|
||||
return Promise.reject(`http error: ${err}`)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ActEnum, IsYetEnum, MarkEnum, MsgEnum, OnlineEnum, RoomTypeEnum, SexEnum } from '@/enums'
|
||||
|
||||
/**响应请求体*/
|
||||
export type Response = {
|
||||
export type ServiceResponse = {
|
||||
/** 成功标识true or false */
|
||||
success: boolean
|
||||
/** 错误码 */
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { URLEnum } from '@/enums'
|
||||
|
||||
const { PROD, VITE_SERVICE_URL } = import.meta.env
|
||||
const { VITE_SERVICE_URL } = import.meta.env
|
||||
// 本地配置到 .env.development 里面修改。生产配置在 .env.production 里面
|
||||
const prefix = PROD ? VITE_SERVICE_URL : ''
|
||||
const prefix = VITE_SERVICE_URL
|
||||
|
||||
export default {
|
||||
getUserInfo: `${prefix + URLEnum.USER}/userInfo`, // 获取用户信息
|
||||
|
||||
@@ -11,6 +11,11 @@ export const useContactStore = defineStore('contact', () => {
|
||||
const requestFriendsList = reactive<RequestFriendItem[]>([])
|
||||
const contactsOptions = reactive({ isLast: false, isLoading: false, cursor: '' })
|
||||
const requestFriendsOptions = reactive({ isLast: false, isLoading: false, cursor: '' })
|
||||
|
||||
/**
|
||||
* 获取联系人列表
|
||||
* @param isFresh 是否刷新
|
||||
*/
|
||||
const getContactList = async (isFresh = false) => {
|
||||
if (!isFresh) {
|
||||
if (contactsOptions.isLast || contactsOptions.isLoading) return
|
||||
@@ -45,6 +50,10 @@ export const useContactStore = defineStore('contact', () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取好友申请列表
|
||||
* @param isFresh 是否刷新
|
||||
*/
|
||||
const getRequestFriendsList = async (isFresh = false) => {
|
||||
if (!isFresh) {
|
||||
if (requestFriendsOptions.isLast || requestFriendsOptions.isLoading) return
|
||||
|
||||
+2
-2
@@ -94,6 +94,7 @@ export const useGroupStore = defineStore('group', () => {
|
||||
roomId: currentRoomId.value
|
||||
}
|
||||
})
|
||||
console.log(res)
|
||||
if (!res) return
|
||||
const data = res
|
||||
const tempNew = cloneDeep(uniqueUserList(refresh ? data.list : [...data.list, ...userList.value]))
|
||||
@@ -112,8 +113,7 @@ export const useGroupStore = defineStore('group', () => {
|
||||
|
||||
// 获取群成员数量统计
|
||||
const getCountStatistic = async () => {
|
||||
const data = await apis.groupDetail({ id: currentRoomId.value })
|
||||
countInfo.value = data
|
||||
countInfo.value = await apis.groupDetail({ id: currentRoomId.value })
|
||||
}
|
||||
|
||||
// 加载更多群成员
|
||||
|
||||
Vendored
+4
@@ -8,6 +8,8 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
ActionBar: typeof import('./../components/windows/ActionBar.vue')['default']
|
||||
AddFriendsModal: typeof import('./../components/common/AddFriendsModal.vue')['default']
|
||||
ApplyList: typeof import('./../components/rightBox/ApplyList.vue')['default']
|
||||
ChatBox: typeof import('./../components/rightBox/chatBox/index.vue')['default']
|
||||
ChatFooter: typeof import('./../components/rightBox/chatBox/ChatFooter.vue')['default']
|
||||
ChatHeader: typeof import('./../components/rightBox/chatBox/ChatHeader.vue')['default']
|
||||
@@ -34,6 +36,7 @@ declare module 'vue' {
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||
NFlex: typeof import('naive-ui')['NFlex']
|
||||
NGradientText: typeof import('naive-ui')['NGradientText']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NIconWrapper: typeof import('naive-ui')['NIconWrapper']
|
||||
NImage: typeof import('naive-ui')['NImage']
|
||||
@@ -57,6 +60,7 @@ declare module 'vue' {
|
||||
NTab: typeof import('naive-ui')['NTab']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NText: typeof import('naive-ui')['NText']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
NVirtualList: typeof import('naive-ui')['NVirtualList']
|
||||
RenderMessage: typeof import('./../components/rightBox/renderMessage/index.vue')['default']
|
||||
|
||||
Vendored
+2
@@ -63,11 +63,13 @@ declare namespace OPT {
|
||||
* @param label 选项名称
|
||||
* @param icon 选项图标
|
||||
* @param click 点击事件
|
||||
* @param visible 显示条件
|
||||
*/
|
||||
type RightMenu = {
|
||||
label: string
|
||||
icon: string
|
||||
click?: (...args: any[]) => void
|
||||
visible?: (...args: any[]) => void
|
||||
} | null
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<n-scrollbar style="max-height: calc(100vh - 70px)">
|
||||
<n-flex
|
||||
@click="handleApply"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
class="my-10px p-12px hover:(bg-[--list-hover-color] cursor-pointer)">
|
||||
<div class="text-(14px [--text-color])">好友通知</div>
|
||||
<svg class="size-16px rotate-270 color-[--text-color]"><use href="#down"></use></svg>
|
||||
</n-flex>
|
||||
<n-tabs type="segment" animated class="mt-4px p-[4px_10px_0px_8px]">
|
||||
<n-tab-pane name="1" tab="好友">
|
||||
<n-scrollbar style="max-height: calc(100vh - 126px)">
|
||||
@@ -7,7 +15,9 @@
|
||||
<ContextMenu @contextmenu="showMenu($event)" @select="handleSelect($event.label)" :menu="menuList">
|
||||
<n-collapse-item title="我的好友" name="1">
|
||||
<template #header-extra>
|
||||
<span class="text-(10px #707070)">0/0</span>
|
||||
<span class="text-(10px #707070)">
|
||||
{{ contactStore.contactsList.length }}/{{ contactStore.contactsList.length }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<!-- 用户框 多套一层div来移除默认的右键事件然后覆盖掉因为margin空隙而导致右键可用 -->
|
||||
@@ -88,6 +98,7 @@ watchEffect(() => {
|
||||
const handleClick = (index: number, type: number) => {
|
||||
detailsShow.value = true
|
||||
activeItem.value = index
|
||||
console.log(index, type)
|
||||
const data = {
|
||||
context: {
|
||||
type: type,
|
||||
@@ -106,6 +117,14 @@ const handleSelect = (event: MouseEvent) => {
|
||||
console.log(event)
|
||||
}
|
||||
|
||||
const handleApply = () => {
|
||||
Mitt.emit(MittEnum.APPLY_SHOW, {
|
||||
context: {
|
||||
type: 'apply'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
detailsShow.value = false
|
||||
Mitt.emit(MittEnum.DETAILS_SHOW, detailsShow.value)
|
||||
|
||||
@@ -71,6 +71,7 @@ const globalStore = useGlobalStore()
|
||||
const { userUid } = useCommon()
|
||||
const scrollbar = ref()
|
||||
const { handleMsgClick, activeIndex, menuList, specialMenuList, handleMsgDblclick } = useMessage()
|
||||
// TODO 艾特我提醒
|
||||
const sessionList = computed(() =>
|
||||
chatStore.sessionList.map((item) => {
|
||||
// 最后一条消息内容
|
||||
@@ -122,7 +123,7 @@ watchEffect(() => {
|
||||
|
||||
onBeforeMount(() => {
|
||||
// 请求回话列表
|
||||
chatStore.getSessionList(true)
|
||||
chatStore.getSessionList()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -142,8 +142,8 @@ import { invoke } from '@tauri-apps/api/core'
|
||||
import { emit } from '@tauri-apps/api/event'
|
||||
|
||||
const settingStore = useSettingStore()
|
||||
const { themes, tips, escClose, chat, page } = settingStore
|
||||
const { showMode } = storeToRefs(settingStore)
|
||||
const { themes, tips, chat, page } = settingStore
|
||||
const { showMode, escClose } = storeToRefs(settingStore)
|
||||
const activeItem = ref<string>(themes.pattern)
|
||||
|
||||
const showText = computed({
|
||||
|
||||
Reference in New Issue
Block a user