From 787ccc59ba89bacc075562a161ecf02bc76ebadc Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期五, 24 七月 2026 17:15:53 +0800
Subject: [PATCH] 银川 1.采购申请添加入库状态查询条件
---
src/views/im/home/pages/conversation/components/message/group-pinned-message.vue | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 207 insertions(+), 0 deletions(-)
diff --git a/src/views/im/home/pages/conversation/components/message/group-pinned-message.vue b/src/views/im/home/pages/conversation/components/message/group-pinned-message.vue
new file mode 100644
index 0000000..b411944
--- /dev/null
+++ b/src/views/im/home/pages/conversation/components/message/group-pinned-message.vue
@@ -0,0 +1,207 @@
+<script lang="ts" setup>
+import type { Message } from '../../../../types'
+
+import { computed, ref, watch } from 'vue'
+
+import { IconifyIcon as Icon } from '#/packages/icons/src'
+
+import { Button, message } from 'ant-design-vue'
+
+import { unpinGroupMessage as apiUnpinGroupMessage } from '#/api/im/group'
+import { getCurrentUserId } from '#/views/im/utils/auth'
+import { ImConversationType, ImGroupMemberRole } from '#/views/im/utils/constants'
+import { resolveConversationLastContent } from '#/views/im/utils/conversation'
+import { getSenderDisplayName, isGroupQuit } from '#/views/im/utils/user'
+
+import { useGroupStore } from '../../../../store/groupStore'
+
+defineOptions({ name: 'ImGroupPinnedMessage' })
+
+const props = defineProps<{
+ /** 褰撳墠缇ょ紪鍙凤紙鑷浠� groupStore 鎷垮畬鏁� Group锛岃窡闅忓搷搴斿紡锛� */
+ groupId: number
+}>()
+
+const emit = defineEmits<{
+ /** 鐐瑰嚮缃《娑堟伅 鈫� 鐖剁骇 MessagePanel 婊氬姩瀹氫綅鍒板師娑堟伅浣嶇疆 */
+ locate: [messageId: number]
+}>()
+
+const groupStore = useGroupStore()
+
+/** 褰撳墠缇わ紙鍚� pinnedMessages锛� */
+const group = computed(() => groupStore.getGroup(props.groupId))
+
+const expanded = ref(false)
+const removingId = ref<null | number>(null)
+
+// 鍒囩兢鏃堕噸缃睍寮� / 绉婚櫎涓姸鎬侊細鏈湴 ref 涓嶈窡闅� groupId锛屽惁鍒欎笂涓�缇�"灞曞紑"鎴�"绉婚櫎涓�"浼氬甫鍒版柊缇�
+watch(
+ () => props.groupId,
+ () => {
+ expanded.value = false
+ removingId.value = null
+ }
+)
+
+/** 褰撳墠缇ょ疆椤舵秷鎭垪琛紙鐩存帴璧� group.value锛岃窡闅忓搷搴斿紡锛� */
+const pinnedMessages = computed<Message[]>(() => group.value?.pinnedMessages ?? [])
+
+/** 椤堕儴鑳跺泭灞曠ず鐨勬渶鏂颁竴鏉★紙鍗冲垪琛ㄦ渶鍚庝竴鏉★紝pin 椤哄簭杩藉姞锛� */
+const latest = computed<Message | null>(
+ () => pinnedMessages.value[pinnedMessages.value.length - 1] ?? null
+)
+
+/** 褰撳墠鐢ㄦ埛鏄惁缇や富 / 绠$悊鍛橈紙鍐冲畾鏄惁鏄剧ず銆岀Щ闄ゃ�嶅叆鍙o級 */
+const canManage = computed(() => {
+ // 鍘嗗彶閫�缇ょ兢锛氭湰鍦扮紦瀛樻畫鐣欐椂涔熶笉缁欍�岀Щ闄ゃ�嶅叆鍙�
+ if (isGroupQuit(group.value)) {
+ return false
+ }
+ const myId = getCurrentUserId()
+ const role = group.value?.members?.find((m) => m.userId === myId)?.role
+ return role === ImGroupMemberRole.OWNER || role === ImGroupMemberRole.ADMIN
+})
+
+/** 椤堕儴鑳跺泭鐐瑰嚮锛氬崟鏉$洿鎺ヨ烦杞師娑堟伅浣嶇疆锛涘鏉″垏鎹㈠睍寮� / 鎶樺彔 */
+function handleTopClick() {
+ if (!latest.value) {
+ return
+ }
+ if (pinnedMessages.value.length === 1) {
+ handleLocate(latest.value)
+ return
+ }
+ expanded.value = !expanded.value
+}
+
+/** 鐐瑰嚮缃《娑堟伅琛� 鈫� 瑙﹀彂璺宠浆 + 鏀惰捣寮瑰嚭灞� */
+function handleLocate(message: Message) {
+ if (!message.id) {
+ return
+ }
+ emit('locate', message.id)
+ expanded.value = false
+}
+
+/** 缃《娑堟伅鍙戦�佷汉鏄剧ず鍚� */
+function getSenderName(message: Message): string {
+ return group.value
+ ? getSenderDisplayName(message.senderId, ImConversationType.GROUP, group.value.id)
+ : ''
+}
+
+/** 缃《娑堟伅棰勮鏂囨湰锛氬鐢ㄤ細璇濇渶鍚庝竴鏉℃憳瑕侀�昏緫锛圼鍥剧墖] / [鏂囦欢] / 鏂囨湰绛夛級 */
+function getPreview(message: Message): string {
+ return group.value
+ ? resolveConversationLastContent(message, ImConversationType.GROUP, group.value.id)
+ : ''
+}
+
+/** 绉婚櫎缃《锛氳皟鍚庣 API锛宭oading 鏈熼棿绂佹閲嶅鐐癸紱鍚庣骞挎挱 GROUP_MESSAGE_UNPIN 鐢� dispatcher 鑷姩鍚屾鏈湴 */
+async function handleRemove(pinnedMessage: Message) {
+ if (!group.value || !pinnedMessage.id || removingId.value !== null) {
+ return
+ }
+ removingId.value = pinnedMessage.id
+ try {
+ await apiUnpinGroupMessage({ id: group.value.id, messageId: pinnedMessage.id })
+ message.success('宸插彇娑堢疆椤�')
+ } finally {
+ removingId.value = null
+ }
+}
+</script>
+
+<template>
+ <!-- 缇よ亰缃《娑堟伅锛氫粎缇よ亰 + 鏈夌疆椤舵椂鏄剧ず锛屾偓鎸傚湪缇よ亰澶撮儴涓嬫柟宸︿笂瑙掞紱涓嶅崰鏁磋锛堝榻愬井淇� PC锛� -->
+ <div
+ v-if="latest"
+ class="im-group-pinned-message relative flex flex-shrink-0 flex-col items-start px-4 pt-1.5 pb-2 bg-[var(--ant-color-fill-secondary)]"
+ >
+ <!-- 椤堕儴鑳跺泭锛氬崟鏉$偣鍑昏烦杞紱澶氭潯鎶樺彔鐐瑰嚮灞曞紑锛涘鏉″睍寮�鐐瑰嚮鎶樺彔 -->
+ <div
+ class="flex items-center gap-1.5 w-[360px] px-3 py-1.5 rounded-[10px] text-13px text-[var(--ant-color-text)] bg-[var(--ant-color-bg-container)] shadow-[0_1px_2px_rgba(0,0,0,0.04)] cursor-pointer hover:bg-[var(--ant-color-fill-tertiary)]"
+ @click="handleTopClick"
+ >
+ <Icon
+ icon="ant-design:pushpin-outlined"
+ :size="14"
+ class="flex-shrink-0 text-[var(--ant-color-warning)]"
+ />
+ <span class="flex-shrink-0 text-[var(--ant-color-text-secondary)]">{{ getSenderName(latest) }}锛�</span>
+ <span class="flex-1 min-w-0 truncate">{{ getPreview(latest) }}</span>
+ <!-- 鍗曟潯锛氱Щ闄ゆ寜閽紱澶氭潯鎶樺彔锛氬叡 N 鏉★紱澶氭潯灞曞紑锛氭敹璧风澶� -->
+ <Button
+ v-if="pinnedMessages.length === 1 && canManage"
+ type="link"
+ size="small"
+ :loading="removingId === latest.id"
+ class="flex-shrink-0 !h-auto !p-0 text-13px"
+ @click.stop="handleRemove(latest)"
+ >
+ 绉婚櫎
+ </Button>
+ <template v-else-if="pinnedMessages.length > 1">
+ <span class="flex-shrink-0 text-[var(--ant-color-text-secondary)] text-12px">
+ 鍏� {{ pinnedMessages.length }} 鏉�
+ </span>
+ <Icon
+ :icon="expanded ? 'ant-design:up-outlined' : 'ant-design:down-outlined'"
+ :size="11"
+ class="flex-shrink-0 text-[var(--ant-color-text-placeholder)]"
+ />
+ </template>
+ </div>
+
+ <!-- 澶氭潯灞曞紑锛氭祬鑹查潰鏉垮寘瑁瑰畬鏁村垪琛紝姣忔潯鐙珛鑳跺泭锛涚偣鍑昏烦杞埌瀵瑰簲娑堟伅浣嶇疆 -->
+ <div
+ v-if="pinnedMessages.length > 1 && expanded"
+ class="im-group-pinned-message__list absolute top-full left-1.5 z-10 flex flex-col gap-2.5 w-[380px] p-3 rounded-xl bg-[var(--ant-color-bg-container)] shadow-[0_6px_16px_rgba(0,0,0,0.12)]"
+ style="margin-top: -1px"
+ >
+ <div
+ v-for="msg in pinnedMessages"
+ :key="msg.id"
+ class="flex items-center gap-1.5 w-full px-3 py-1.5 rounded-[10px] text-13px text-[var(--ant-color-text)] bg-[var(--ant-color-fill-secondary)] cursor-pointer hover:bg-[var(--ant-color-bg-container)]"
+ @click="handleLocate(msg)"
+ >
+ <Icon
+ icon="ant-design:pushpin-outlined"
+ :size="14"
+ class="flex-shrink-0 text-[var(--ant-color-warning)]"
+ />
+ <span class="flex-shrink-0 text-[var(--ant-color-text-secondary)]">
+ {{ getSenderName(msg) }}锛�
+ </span>
+ <span class="flex-1 min-w-0 truncate">{{ getPreview(msg) }}</span>
+ <Button
+ v-if="canManage"
+ type="link"
+ size="small"
+ :loading="removingId === msg.id"
+ class="flex-shrink-0 !h-auto !p-0 text-13px"
+ @click.stop="handleRemove(msg)"
+ >
+ 绉婚櫎
+ </Button>
+ </div>
+ </div>
+ </div>
+</template>
+
+<style scoped>
+/* 寮瑰嚭灞傛湞涓婄殑涓夎绠ご锛涜蛋 ::before + 4 杈� border 閰嶈壊鐢伙紝棰滆壊璺熷脊鍑哄眰 background 涓�鑷� */
+.im-group-pinned-message__list::before {
+ content: '';
+ position: absolute;
+ top: -8px;
+ left: 184px;
+ width: 0;
+ height: 0;
+ border-left: 8px solid transparent;
+ border-right: 8px solid transparent;
+ border-bottom: 8px solid var(--ant-color-bg-container);
+ filter: drop-shadow(0 -2px 1px rgba(0, 0, 0, 0.04));
+}
+</style>
--
Gitblit v1.9.3