236236 <template v-else>
237237 <div v-for="contact in filteredContacts" :key="contact.id"
238238 class="px-3 cursor-pointer transition-colors duration-150 border-b border-gray-100 h-[calc(80px/var(--dpr))] flex items-center"
239- :class="selectedContact?.id === contact.id ? 'bg-[#DEDEDE] hover:bg-[#d3d3d3]' : 'hover:bg-[#eaeaea]'"
239+ :class="contact.isTop
240+ ? (selectedContact?.id === contact.id
241+ ? 'bg-[#D2D2D2] hover:bg-[#C7C7C7]'
242+ : 'bg-[#EAEAEA] hover:bg-[#DEDEDE]')
243+ : (selectedContact?.id === contact.id
244+ ? 'bg-[#DEDEDE] hover:bg-[#d3d3d3]'
245+ : 'hover:bg-[#eaeaea]')"
240246 @click="selectContact(contact)">
241247 <div class="flex items-center space-x-3 w-full">
242248 <!-- 联系人头像 -->
501507 :fromAvatar="message.fromAvatar"
502508 :from="message.from"
503509 :isSent="message.isSent"
510+ :variant="message.linkCardVariant || 'default'"
504511 />
505512 <div v-else-if="message.renderType === 'file'"
506513 class="wechat-redpacket-card wechat-special-card wechat-file-card msg-radius"
651658 class="hidden"
652659 ></audio>
653660 </div>
654- <div v-else class="line-clamp-2">
655- <span v-if="message.quoteTitle">{{ message.quoteTitle }}:</span>
656- <span
657- v-if="message.quoteContent && !(isQuotedImage(message) && message.quoteTitle && message.quoteImageUrl && !message._quoteImageError)"
658- :class="message.quoteTitle ? 'ml-1' : ''"
659- >
660- {{ message.quoteContent }}
661- </span>
661+ <div v-else class="min-w-0 flex items-start">
662+ <template v-if="isQuotedLink(message)">
663+ <div class="line-clamp-2 min-w-0 flex-1">
664+ <span v-if="message.quoteTitle">{{ message.quoteTitle }}:</span>
665+ <span
666+ v-if="getQuotedLinkText(message)"
667+ :class="message.quoteTitle ? 'ml-1' : ''"
668+ >
669+ 🔗 {{ getQuotedLinkText(message) }}
670+ </span>
671+ </div>
672+ </template>
673+ <template v-else>
674+ <div class="line-clamp-2 min-w-0 flex-1">
675+ <span v-if="message.quoteTitle">{{ message.quoteTitle }}:</span>
676+ <span
677+ v-if="message.quoteContent && !(isQuotedImage(message) && message.quoteTitle && message.quoteImageUrl && !message._quoteImageError)"
678+ :class="message.quoteTitle ? 'ml-1' : ''"
679+ >
680+ {{ message.quoteContent }}
681+ </span>
682+ </div>
683+ </template>
662684 </div>
663685 </div>
664686 <div
665- v-if="isQuotedImage(message) && message.quoteImageUrl && !message._quoteImageError"
666- class="ml-2 my-2 flex-shrink-0 w-[45px] h-[45px] rounded overflow-hidden cursor-pointer"
687+ v-if="isQuotedLink(message) && message.quoteThumbUrl && !message._quoteThumbError"
688+ class="ml-2 my-2 flex-shrink-0 max-w-[98px] max-h-[49px] overflow-hidden flex items-center justify-center cursor-pointer"
689+ @click.stop="openImagePreview(message.quoteThumbUrl)"
690+ >
691+ <img
692+ :src="message.quoteThumbUrl"
693+ alt="引用链接缩略图"
694+ class="max-h-[49px] w-auto max-w-[98px] object-contain"
695+ loading="lazy"
696+ decoding="async"
697+ referrerpolicy="no-referrer"
698+ @error="onQuoteThumbError(message)"
699+ />
700+ </div>
701+ <div
702+ v-if="!isQuotedLink(message) && isQuotedImage(message) && message.quoteImageUrl && !message._quoteImageError"
703+ class="ml-2 my-2 flex-shrink-0 max-w-[98px] max-h-[49px] overflow-hidden flex items-center justify-center cursor-pointer"
667704 @click.stop="openImagePreview(message.quoteImageUrl)"
668705 >
669706 <img
670707 :src="message.quoteImageUrl"
671708 alt="引用图片"
672- class="w-full h-full object-contain"
709+ class="max-h-[49px] w-auto max-w-[98px] object-contain"
673710 loading="lazy"
674711 decoding="async"
675712 @error="onQuoteImageError(message)"
@@ -3226,12 +3263,31 @@ const isQuotedImage = (message) => {
32263263 return false
32273264}
32283265
3266+ const isQuotedLink = (message) => {
3267+ const t = String(message?.quoteType || '').trim()
3268+ if (t === '49') return true
3269+ return /^\[链接\]\s*/.test(String(message?.quoteContent || '').trim())
3270+ }
3271+
3272+ const getQuotedLinkText = (message) => {
3273+ const raw = String(message?.quoteContent || '').trim()
3274+ if (!raw) return ''
3275+ const stripped = raw.replace(/^\[链接\]\s*/u, '').trim()
3276+ return stripped || raw
3277+ }
3278+
32293279const onQuoteImageError = (message) => {
32303280 try {
32313281 if (message) message._quoteImageError = true
32323282 } catch {}
32333283}
32343284
3285+ const onQuoteThumbError = (message) => {
3286+ try {
3287+ if (message) message._quoteThumbError = true
3288+ } catch {}
3289+ }
3290+
32353291const playQuoteVoice = (message) => {
32363292 playVoice({ id: getQuoteVoiceId(message) })
32373293}
@@ -3969,7 +4025,7 @@ const getTransferTitle = (message) => {
39694025 if (message.transferStatus) return message.transferStatus
39704026 switch (paySubType) {
39714027 case '1': return '转账'
3972- case '3': return message.isSent ? '已收款 ' : '已被接收 '
4028+ case '3': return message.isSent ? '已被接收 ' : '已收款 '
39734029 case '8': return '发起转账'
39744030 case '4': return '已退还'
39754031 case '9': return '已被退还'
@@ -4136,6 +4192,7 @@ const loadSessionsForSelectedAccount = async () => {
41364192 lastMessageTime: s.lastMessageTime || '',
41374193 unreadCount: s.unreadCount || 0,
41384194 isGroup: !!s.isGroup,
4195+ isTop: !!s.isTop,
41394196 username: s.username
41404197 }))
41414198
@@ -4209,6 +4266,7 @@ const refreshSessionsForSelectedAccount = async ({ sourceOverride } = {}) => {
42094266 lastMessageTime: s.lastMessageTime || '',
42104267 unreadCount: s.unreadCount || 0,
42114268 isGroup: !!s.isGroup,
4269+ isTop: !!s.isTop,
42124270 username: s.username
42134271 }))
42144272
@@ -4401,6 +4459,19 @@ const normalizeMessage = (msg) => {
44014459 ].filter(Boolean)
44024460 return parts.length ? `${mediaBase}/api/chat/media/image?${parts.join('&')}` : ''
44034461 })()
4462+ const quoteThumbUrl = (() => {
4463+ const raw = isUsableMediaUrl(msg.quoteThumbUrl) ? normalizeMaybeUrl(msg.quoteThumbUrl) : ''
4464+ if (!raw) return ''
4465+ if (/^\/api\/chat\/media\//i.test(raw) || /^blob:/i.test(raw) || /^data:/i.test(raw)) return raw
4466+ if (!/^https?:\/\//i.test(raw)) return raw
4467+ try {
4468+ const host = new URL(raw).hostname.toLowerCase()
4469+ if (host.endsWith('.qpic.cn') || host.endsWith('.qlogo.cn')) {
4470+ return `${mediaBase}/api/chat/media/proxy_image?url=${encodeURIComponent(raw)}`
4471+ }
4472+ } catch {}
4473+ return raw
4474+ })()
44044475
44054476 return {
44064477 id: msg.id,
@@ -4443,17 +4514,22 @@ const normalizeMessage = (msg) => {
44434514 quoteVoiceLength: msg.quoteVoiceLength || '',
44444515 quoteVoiceUrl,
44454516 quoteImageUrl: quoteImageUrl || '',
4517+ quoteThumbUrl: quoteThumbUrl || '',
44464518 _quoteImageError: false,
4519+ _quoteThumbError: false,
44474520 amount: msg.amount || '',
44484521 coverUrl: msg.coverUrl || '',
44494522 fileSize: msg.fileSize || '',
44504523 fileMd5: msg.fileMd5 || '',
44514524 paySubType: msg.paySubType || '',
44524525 transferStatus: msg.transferStatus || '',
4453- transferReceived: msg.paySubType === '3' || msg.transferStatus === '已收款',
4526+ transferReceived: msg.paySubType === '3' || msg.transferStatus === '已收款' || msg.transferStatus === '已被接收' ,
44544527 voiceUrl: normalizedVoiceUrl || '',
44554528 voiceDuration: msg.voiceLength || msg.voiceDuration || '',
44564529 preview: normalizedLinkPreviewUrl || '',
4530+ linkType: String(msg.linkType || '').trim(),
4531+ linkStyle: String(msg.linkStyle || '').trim(),
4532+ linkCardVariant: String(msg.linkStyle || '').trim() === 'cover' ? 'cover' : 'default',
44574533 from: String(msg.from || '').trim(),
44584534 fromUsername,
44594535 fromAvatar,
@@ -5331,7 +5407,8 @@ const LinkCard = defineComponent({
53315407 preview: { type: String, default: '' },
53325408 fromAvatar: { type: String, default: '' },
53335409 from: { type: String, default: '' },
5334- isSent: { type: Boolean, default: false }
5410+ isSent: { type: Boolean, default: false },
5411+ variant: { type: String, default: 'default' }
53355412 },
53365413 setup(props) {
53375414 const getFromText = () => {
@@ -5356,6 +5433,65 @@ const LinkCard = defineComponent({
53565433 return t ? (Array.from(t)[0] || '') : ''
53575434 })()
53585435 const fromAvatarUrl = String(props.fromAvatar || '').trim()
5436+ const isCoverVariant = String(props.variant || '').trim() === 'cover'
5437+
5438+ if (isCoverVariant) {
5439+ const fromRow = h('div', { class: 'wechat-link-cover-from' }, [
5440+ h('div', { class: 'wechat-link-cover-from-avatar', 'aria-hidden': 'true' }, [
5441+ fromAvatarText || '\u200B',
5442+ fromAvatarUrl ? h('img', {
5443+ src: fromAvatarUrl,
5444+ alt: '',
5445+ class: 'wechat-link-cover-from-avatar-img',
5446+ referrerpolicy: 'no-referrer',
5447+ onError: (e) => { try { e?.target && (e.target.style.display = 'none') } catch {} }
5448+ }) : null
5449+ ].filter(Boolean)),
5450+ h('div', { class: 'wechat-link-cover-from-name' }, fromText || '\u200B')
5451+ ])
5452+
5453+ return h(
5454+ 'a',
5455+ {
5456+ href: props.href,
5457+ target: '_blank',
5458+ rel: 'noreferrer',
5459+ class: [
5460+ 'wechat-link-card-cover',
5461+ 'wechat-special-card',
5462+ 'msg-radius',
5463+ props.isSent ? 'wechat-special-sent-side' : ''
5464+ ].filter(Boolean).join(' '),
5465+ style: {
5466+ width: '137px',
5467+ minWidth: '137px',
5468+ maxWidth: '137px',
5469+ display: 'flex',
5470+ flexDirection: 'column',
5471+ boxSizing: 'border-box',
5472+ flex: '0 0 auto',
5473+ background: '#fff',
5474+ border: 'none',
5475+ boxShadow: 'none',
5476+ textDecoration: 'none',
5477+ outline: 'none'
5478+ }
5479+ },
5480+ [
5481+ props.preview ? h('div', { class: 'wechat-link-cover-image-wrap' }, [
5482+ h('img', {
5483+ src: props.preview,
5484+ alt: props.heading || '链接封面',
5485+ class: 'wechat-link-cover-image',
5486+ referrerpolicy: 'no-referrer'
5487+ }),
5488+ fromRow,
5489+ ]) : fromRow,
5490+ h('div', { class: 'wechat-link-cover-title' }, props.heading || props.href)
5491+ ].filter(Boolean)
5492+ )
5493+ }
5494+
53595495 return h(
53605496 'a',
53615497 {
@@ -5930,11 +6066,11 @@ const LinkCard = defineComponent({
59306066
59316067/* 已领取的转账样式 */
59326068.wechat-transfer-received {
5933- background: #f8e2c6 ;
6069+ background: #FDCE9D ;
59346070}
59356071
59366072.wechat-transfer-received::after {
5937- background: #f8e2c6 ;
6073+ background: #FDCE9D ;
59386074}
59396075
59406076.wechat-transfer-received .wechat-transfer-amount,
@@ -6258,6 +6394,111 @@ const LinkCard = defineComponent({
62586394 white-space: nowrap;
62596395}
62606396
6397+ /* 链接封面卡片(170x230 图 + 60 底栏) */
6398+ :deep(.wechat-link-card-cover) {
6399+ width: 137px;
6400+ min-width: 137px;
6401+ max-width: 137px;
6402+ background: #fff;
6403+ display: flex;
6404+ flex-direction: column;
6405+ box-sizing: border-box;
6406+ border: none;
6407+ box-shadow: none;
6408+ outline: none;
6409+ cursor: pointer;
6410+ text-decoration: none;
6411+ transition: background-color 0.15s ease;
6412+ }
6413+
6414+ :deep(.wechat-link-card-cover:hover) {
6415+ background: #f5f5f5;
6416+ }
6417+
6418+ :deep(.wechat-link-cover-image-wrap) {
6419+ width: 137px;
6420+ height: 180px;
6421+ position: relative;
6422+ overflow: hidden;
6423+ border-radius: 4px 4px 0 0;
6424+ background: #f2f2f2;
6425+ flex-shrink: 0;
6426+ }
6427+
6428+ :deep(.wechat-link-cover-image) {
6429+ width: 100%;
6430+ height: 100%;
6431+ object-fit: cover;
6432+ object-position: center;
6433+ display: block;
6434+ }
6435+
6436+ /* 仅公众号封面卡片去掉菱形尖角,其它消息保持原样 */
6437+ :deep(.wechat-link-card-cover.wechat-special-card)::after {
6438+ content: none !important;
6439+ }
6440+
6441+ :deep(.wechat-link-cover-from) {
6442+ height: 30px;
6443+ display: flex;
6444+ align-items: center;
6445+ gap: 6px;
6446+ padding: 0 10px;
6447+ box-sizing: border-box;
6448+ position: absolute;
6449+ left: 0;
6450+ right: 0;
6451+ bottom: 0;
6452+ background: transparent;
6453+ flex-shrink: 0;
6454+ }
6455+
6456+ :deep(.wechat-link-cover-from-avatar) {
6457+ width: 18px;
6458+ height: 18px;
6459+ border-radius: 50%;
6460+ background: #111;
6461+ color: #fff;
6462+ font-size: 11px;
6463+ line-height: 18px;
6464+ text-align: center;
6465+ flex-shrink: 0;
6466+ position: relative;
6467+ overflow: hidden;
6468+ }
6469+
6470+ :deep(.wechat-link-cover-from-avatar-img) {
6471+ position: absolute;
6472+ inset: 0;
6473+ width: 100%;
6474+ height: 100%;
6475+ object-fit: cover;
6476+ display: block;
6477+ }
6478+
6479+ :deep(.wechat-link-cover-from-name) {
6480+ font-size: 12px;
6481+ color: #f3f3f3;
6482+ overflow: hidden;
6483+ text-overflow: ellipsis;
6484+ white-space: nowrap;
6485+ }
6486+
6487+ :deep(.wechat-link-cover-title) {
6488+ height: 50px;
6489+ padding: 7px 10px 0;
6490+ box-sizing: border-box;
6491+ font-size: 12px;
6492+ line-height: 1.24;
6493+ color: #1a1a1a;
6494+ display: -webkit-box;
6495+ -webkit-line-clamp: 2;
6496+ -webkit-box-orient: vertical;
6497+ overflow: hidden;
6498+ word-break: break-word;
6499+ flex-shrink: 0;
6500+ }
6501+
62616502/* 隐私模式模糊效果 */
62626503.privacy-blur {
62636504 filter: blur(9px);
0 commit comments