fix(im): 修复 vben 三端 IM 组件库属性适配

- 修复 web-ele Element Plus 属性误用
  - Switch 使用 model-value 展示开关状态
  - Badge 使用 value 展示未读角标
  - Image 使用 preview-src-list 恢复图片预览
  - TabPane 使用 label/name 恢复已读状态切换
  - Select 改用 ElOption 渲染频道和素材选项
  - Popover 使用 v-model:visible 控制显示
  - Input 使用 show-word-limit 展示字数统计
  - Descriptions/Table 使用 border 展示边框
- 清理 web-ele 无效 closable 和误用 content 属性
- 清理 web-antdv-next 直接组件的 deprecated prop
  - destroy-on-close 改为 destroy-on-hidden
  - mask-closable 改为 mask.closable
  - body-style 改为 styles.body
pull/367/head
YunaiV 2026-06-19 17:39:37 -07:00
parent fac0190ca8
commit 1d1b0a1d0f
39 changed files with 75 additions and 62 deletions

View File

@ -163,7 +163,7 @@ async function handleSubmitApply() {
v-model:open="visible"
:title="dialogTitle"
width="480px"
:mask-closable="false"
:mask="{ closable: false }"
:footer="step === 'apply' ? undefined : null"
>
<!-- 第一层搜索 + 用户列表 -->

View File

@ -92,7 +92,7 @@ async function handleOk() {
v-model:open="visible"
title="设置群管理员"
width="700px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -114,7 +114,7 @@ async function handleOk() {
v-model:open="visible"
title="发起群聊"
width="720px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -137,7 +137,7 @@ async function handleOk() {
v-model:open="visible"
title="添加群成员"
width="720px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -69,7 +69,7 @@ async function handleOk() {
v-model:open="visible"
title="移出群成员"
width="700px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -59,7 +59,7 @@ defineExpose({ open })
<template>
<!-- 禁言时长选择弹窗 -->
<Modal v-model:open="visible" title="设置禁言" width="560px" :mask-closable="false">
<Modal v-model:open="visible" title="设置禁言" width="560px" :mask="{ closable: false }">
<div class="flex flex-col gap-4">
<!-- 成员信息卡 FriendAddDialog user 卡保持一致的浅色背景 -->
<div

View File

@ -90,7 +90,7 @@ async function handleOk() {
v-model:open="visible"
title="选择新群主"
width="700px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -167,7 +167,7 @@ function updateLocalResult(id: number, handleResult: number) {
title="进群申请"
width="560px"
:footer="null"
:mask-closable="false"
:mask="{ closable: false }"
class="im-group-request-list__dialog"
>
<Spin :spinning="loading" wrapper-class-name="w-full">

View File

@ -81,7 +81,7 @@ function handleOk() {
v-model:open="visible"
:title="title"
width="720px"
:mask-closable="false"
:mask="{ closable: false }"
class="im-picker-dialog"
>
<div class="h-[480px]">

View File

@ -221,7 +221,7 @@ async function handleCreateGroupAndSend() {
<Modal
v-model:open="visible"
width="720px"
:mask-closable="false"
:mask="{ closable: false }"
:footer="view === 'conversation' ? null : undefined"
class="im-picker-dialog im-recommend-dialog"
>

View File

@ -303,7 +303,7 @@ async function handleCreateGroupAndSend() {
<Modal
v-model:open="visible"
width="720px"
:mask-closable="false"
:mask="{ closable: false }"
:footer="view === 'conversation' ? null : undefined"
class="im-picker-dialog im-forward-dialog"
>

View File

@ -61,7 +61,7 @@ function handleClose() {
v-model:open="visible"
width="560px"
:footer="null"
:mask-closable="false"
:mask="{ closable: false }"
class="im-merge-detail-dialog"
:closable="true"
@close="handleClose"

View File

@ -116,7 +116,7 @@ const onClick = async () => {
:footer="null"
width="100vw"
wrap-class-name="im-material-detail-modal"
destroy-on-close
destroy-on-hidden
>
<Spin :spinning="detailLoading" wrapper-class-name="w-full">
<div class="material-detail-body max-w-[720px] mx-auto px-5 pt-6 pb-20 min-h-[60vh]">

View File

@ -148,7 +148,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
<Drawer
v-model:open="visible"
:title="title"
destroy-on-close
destroy-on-hidden
:styles="{ wrapper: { width: '65%' } }"
@after-open-change="handleOpenChange"
>

View File

@ -258,7 +258,7 @@ defineExpose({ open: openModal });
v-model:open="open"
title="群选择"
width="70%"
:destroy-on-close="true"
:destroy-on-hidden="true"
@ok="handleConfirm"
@cancel="closeModal"
>

View File

@ -65,7 +65,7 @@ defineExpose({ open });
</script>
<template>
<Drawer v-model:open="visible" destroy-on-close title="群详情" :styles="{ wrapper: { width: '900px' } }">
<Drawer v-model:open="visible" destroy-on-hidden title="群详情" :styles="{ wrapper: { width: '900px' } }">
<Descriptions bordered :column="2">
<DescriptionsItem label="群编号">{{ detail.id }}</DescriptionsItem>
<DescriptionsItem label="群名称">{{ detail.name }}</DescriptionsItem>

View File

@ -51,7 +51,7 @@ defineExpose({ open });
</script>
<template>
<Drawer v-model:open="visible" destroy-on-close title="通话记录详情" :styles="{ wrapper: { width: '900px' } }">
<Drawer v-model:open="visible" destroy-on-hidden title="通话记录详情" :styles="{ wrapper: { width: '900px' } }">
<Descriptions bordered :column="2">
<DescriptionsItem label="编号">{{ detail.id }}</DescriptionsItem>
<DescriptionsItem label="业务通话编号">{{ detail.room }}</DescriptionsItem>

View File

@ -104,7 +104,7 @@ onMounted(loadData);
</script>
<template>
<Card :body-style="{ padding: '12px 16px 16px' }" :title="titleMap[type]">
<Card :styles="{ body: { padding: '12px 16px 16px' } }" :title="titleMap[type]">
<div class="relative min-h-[320px]">
<EchartsUI ref="chartRef" height="320px" />
<div

View File

@ -78,7 +78,7 @@ const cards = computed(() => {
<template>
<div class="grid grid-cols-4 gap-4 max-xl:grid-cols-2 max-md:grid-cols-1">
<Card v-for="card in cards" :key="card.title" :body-style="{ padding: '16px' }">
<Card v-for="card in cards" :key="card.title" :styles="{ body: { padding: '16px' } }">
<div class="flex items-center">
<div
class="mr-3 flex size-12 shrink-0 items-center justify-center rounded"

View File

@ -115,7 +115,7 @@ onMounted(loadData);
</script>
<template>
<Card :body-style="{ padding: '12px 16px 16px' }">
<Card :styles="{ body: { padding: '12px 16px 16px' } }">
<template #title>
<div class="flex items-center justify-between">
<span>{{ title }}</span>

View File

@ -267,7 +267,7 @@ type="textarea"
v-model="applyContent"
:rows="3"
:maxlength="255"
show-count
show-word-limit
placeholder="请填写申请理由"
/>

View File

@ -145,7 +145,7 @@ async function handleJoin() {
<!-- 仅当该群有活跃通话时显示点击胶囊条展开 popover 看在通话成员 + 加入 -->
<div v-if="activeCall" class="flex-shrink-0 px-4 pb-2 bg-[var(--ant-color-fill-secondary)]">
<ElPopover
v-model="popoverVisible"
v-model:visible="popoverVisible"
placement="bottom-start"
:width="280"
trigger="click"

View File

@ -78,7 +78,7 @@ const goProfile = () => router.push({ name: 'Profile' })
>
<ElBadge
v-if="item.name === 'ImHomeConversation' && totalUnread > 0"
:count="totalUnread"
:value="totalUnread"
:max="99"
class="tool-bar__badge"
>
@ -86,7 +86,7 @@ const goProfile = () => router.push({ name: 'Profile' })
</ElBadge>
<ElBadge
v-else-if="item.name === 'ImHomeContact' && unhandledRequestCount > 0"
:count="unhandledRequestCount"
:value="unhandledRequestCount"
:max="99"
class="tool-bar__badge"
>

View File

@ -110,7 +110,9 @@ function handleClick(e: MouseEvent) {
v-if="url && previewable"
class="block overflow-hidden"
:src="url"
:preview="{ src: url, zIndex: previewZIndex }"
:preview-src-list="[url]"
:preview-teleported="true"
:z-index="previewZIndex"
:style="imgStyle"
/>
<img

View File

@ -79,7 +79,7 @@ async function handleLoadMore() {
<!-- 红点未处理且别人加我的统一走 store getter避免本地 computed store 双口径 -->
<ElBadge
v-if="friendStore.getUnhandledRequestCount > 0"
:count="friendStore.getUnhandledRequestCount"
:value="friendStore.getUnhandledRequestCount"
:max="99"
class="mr-2"
/>

View File

@ -496,7 +496,7 @@ function handleOpenTransferOwner() {
<!-- 群聊名称群主可改 -->
<ElPopover
v-if="isOwner"
v-model="namePopoverVisible"
v-model:visible="namePopoverVisible"
trigger="click"
placement="left-start"
:width="280"
@ -528,7 +528,7 @@ function handleOpenTransferOwner() {
<!-- 群公告群主可改内容可能很长 > chevron 表示可展开编辑 -->
<ElPopover
v-if="isOwner"
v-model="noticePopoverVisible"
v-model:visible="noticePopoverVisible"
trigger="click"
placement="left-start"
:width="320"
@ -560,7 +560,7 @@ function handleOpenTransferOwner() {
type="textarea"
:rows="4"
:maxlength="1024"
show-count
show-word-limit
placeholder="请输入群公告"
/>
<div class="flex justify-end gap-2">
@ -586,7 +586,7 @@ function handleOpenTransferOwner() {
<!-- 备注仅自己可见保存后会替换会话列表 / 顶部群名展示历史退群群隐藏改备注走 updateGroupMember已退群会被后端拒 -->
<ElPopover
v-if="!isQuitGroup"
v-model="groupRemarkPopoverVisible"
v-model:visible="groupRemarkPopoverVisible"
trigger="click"
placement="left-start"
:width="280"
@ -613,7 +613,7 @@ function handleOpenTransferOwner() {
type="textarea"
:rows="3"
:maxlength="64"
show-count
show-word-limit
placeholder="仅自己可见"
/>
<div class="flex justify-end gap-2">
@ -626,7 +626,7 @@ function handleOpenTransferOwner() {
<!-- 我在本群的昵称任何成员都能改自己的历史退群群隐藏 updateGroupMember已退群会被后端拒 -->
<ElPopover
v-if="!isQuitGroup"
v-model="remarkPopoverVisible"
v-model:visible="remarkPopoverVisible"
trigger="click"
placement="left-start"
:width="280"
@ -695,16 +695,16 @@ function handleOpenTransferOwner() {
<div class="bg-[var(--ant-color-bg-container)]">
<div class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">消息免打扰</span>
<ElSwitch :checked="!!conversation?.silent" @change="onMutedChange" />
<ElSwitch :model-value="!!conversation?.silent" @change="onMutedChange" />
</div>
<div class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">置顶聊天</span>
<ElSwitch :checked="!!conversation?.top" @change="onTopChange" />
<ElSwitch :model-value="!!conversation?.top" @change="onTopChange" />
</div>
<!-- 全群禁言仅群主或管理员可操作 -->
<div v-if="isOwnerOrAdmin" class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">全群禁言</span>
<ElSwitch :checked="!!currentMutedAll" @change="onMuteAllChange" />
<ElSwitch :model-value="!!currentMutedAll" @change="onMuteAllChange" />
</div>
</div>
@ -716,7 +716,7 @@ function handleOpenTransferOwner() {
<!-- 进群审批仅群主可操作开启后普通成员的申请邀请路径都需群主 / 管理员同意群主 / 管理员邀请直进 -->
<div v-if="isOwner" class="im-conversation-group-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">进群需要群主 / 群管理确认</span>
<ElSwitch :checked="!!group.joinApproval" @change="handleJoinApprovalChange" />
<ElSwitch :model-value="!!group.joinApproval" @change="handleJoinApprovalChange" />
</div>
<!-- 进群申请子项仅当开启审批 + 当前用户是 owner / admin 时出现点击进列表 dialog -->
<div

View File

@ -183,7 +183,7 @@ function handleGroupCreated(groupId: number) {
<!-- 备注仅自己可见点击弹 popover 编辑保存后立即刷新本抽屉 + 会话列表展示名 -->
<div class="bg-[var(--ant-color-bg-container)]">
<ElPopover
v-model="displayNamePopoverVisible"
v-model:visible="displayNamePopoverVisible"
trigger="click"
placement="left-start"
:width="280"
@ -208,7 +208,7 @@ function handleGroupCreated(groupId: number) {
<ElInput
v-model="editDisplayName"
:maxlength="16"
show-count
show-word-limit
placeholder="请输入备注名"
/>
<div class="flex justify-end gap-2">
@ -244,11 +244,11 @@ function handleGroupCreated(groupId: number) {
<div class="bg-[var(--ant-color-bg-container)]">
<div class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">消息免打扰</span>
<ElSwitch :checked="!!conversation?.silent" @change="handleMutedChange" />
<ElSwitch :model-value="!!conversation?.silent" @change="handleMutedChange" />
</div>
<div class="im-conversation-private-side__row flex items-center justify-between gap-3 px-4 py-[13px] text-14px min-h-6 transition-colors duration-150">
<span class="flex-shrink-0 text-14px text-[var(--ant-color-text)]">置顶聊天</span>
<ElSwitch :checked="!!conversation?.top" @change="handleTopChange" />
<ElSwitch :model-value="!!conversation?.top" @change="handleTopChange" />
</div>
</div>
</div>

View File

@ -60,10 +60,8 @@ function handleClose() {
<ElDialog
v-model="visible"
width="560px"
:close-on-click-modal="false"
class="im-merge-detail-dialog"
:closable="true"
@close="handleClose"
>
<template #header>

View File

@ -208,7 +208,8 @@ onBeforeUnmount(() => {
<ElImage
class="max-w-[220px] rounded cursor-zoom-in"
:src="imagePayload.thumbnailUrl || imagePayload.url"
:preview="isUploading ? false : { src: imagePayload.url }"
:preview-src-list="isUploading ? [] : [imagePayload.url]"
:preview-teleported="true"
/>
<div
v-if="isUploading"

View File

@ -565,7 +565,7 @@ function locateMessage(messageId: number) {
</span>
<!-- 日期el-popover el-calendar确认后才落筛选 -->
<ElPopover
v-model="datePopoverVisible"
v-model:visible="datePopoverVisible"
trigger="click"
placement="bottom"
:width="320"
@ -596,7 +596,7 @@ function locateMessage(messageId: number) {
<!-- 群成员仅群聊popover 内自带搜索 + GroupMember 列表 -->
<ElPopover
v-if="isGroup"
v-model="memberPopoverVisible"
v-model:visible="memberPopoverVisible"
trigger="click"
placement="bottom"
:width="320"

View File

@ -567,7 +567,7 @@ watch(
<!-- 通话入口私聊弹语音 / 视频popover群聊直接进选人弹窗 -->
<ElPopover
v-if="isPrivate"
v-model="callPopoverVisible"
v-model:visible="callPopoverVisible"
placement="bottom-end"
:width="140"
trigger="click"

View File

@ -123,7 +123,7 @@ async function loadReadUsers() {
- 未读名单 = 当前群成员 自己 已退群 已读集合前端聚合后端只回已读 userId
-->
<ElPopover
v-model="popVisible"
v-model:visible="popVisible"
placement="left"
trigger="click"
:width="320"
@ -138,7 +138,7 @@ async function loadReadUsers() {
</template>
<ElTabs v-model="activeTab">
<ElTabPane :tab="`已读(${readMembers.length})`" key="read">
<ElTabPane :label="`已读(${readMembers.length})`" name="read">
<PagedScroller :items="readMembers" :page-size="20" item-key="userId" class="h-75">
<template #default="{ item }">
<GroupMember :member="item as GroupMemberLite" :height="40" :clickable="false" />
@ -151,7 +151,7 @@ async function loadReadUsers() {
{{ groupMembers.length === 0 ? '群成员未加载' : '暂无已读' }}
</div>
</ElTabPane>
<ElTabPane :tab="`未读(${unreadMembers.length})`" key="unread">
<ElTabPane :label="`未读(${unreadMembers.length})`" name="unread">
<PagedScroller :items="unreadMembers" :page-size="20" item-key="userId" class="h-75">
<template #default="{ item }">
<GroupMember :member="item as GroupMemberLite" :height="40" :clickable="false" />

View File

@ -3,7 +3,7 @@ import type { ImManagerChannelApi } from '#/api/im/manager/channel';
import { computed, onMounted, ref } from 'vue';
import { ElSelect } from 'element-plus'
import { ElOption, ElSelect } from 'element-plus'
import { getSimpleChannelList } from '#/api/im/manager/channel';
@ -59,8 +59,14 @@ onMounted(loadChannelList);
:clearable="clearable"
:disabled="disabled"
:loading="loading"
:options="options"
:placeholder="placeholder"
class="w-full"
/>
>
<ElOption
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</ElSelect>
</template>

View File

@ -3,7 +3,7 @@ import type { ImManagerChannelMaterialApi } from '#/api/im/manager/channel/mater
import { computed, ref, watch } from 'vue';
import { ElSelect } from 'element-plus'
import { ElOption, ElSelect } from 'element-plus'
import { getSimpleManagerChannelMaterialList } from '#/api/im/manager/channel/material';
@ -64,8 +64,14 @@ watch(() => props.channelId, loadMaterialList, { immediate: true });
:clearable="clearable"
:disabled="!channelId"
:loading="loading"
:options="options"
:placeholder="placeholder"
class="w-full"
/>
>
<ElOption
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</ElSelect>
</template>

View File

@ -58,7 +58,7 @@ type="textarea"
:maxlength="200"
:rows="3"
placeholder="请输入封禁原因"
show-count
show-word-limit
/>
</ElFormItem>
</ElForm>

View File

@ -44,7 +44,7 @@ defineExpose({ open });
<template>
<ElDrawer v-model="visible" destroy-on-close title="群详情" width="900">
<ElDescriptions bordered :column="2">
<ElDescriptions border :column="2">
<ElDescriptionsItem label="群编号">{{ detail.id }}</ElDescriptionsItem>
<ElDescriptionsItem label="群名称">{{ detail.name }}</ElDescriptionsItem>
<ElDescriptionsItem label="头像">
@ -84,7 +84,7 @@ defineExpose({ open });
<ElTable
v-loading="loading"
:data="filteredMembers"
bordered
border
row-key="userId"
size="small"
>

View File

@ -36,7 +36,7 @@ defineExpose({ open });
<template>
<ElDialog v-model="visible" title="群聊消息详情" width="700px">
<ElDescriptions bordered :column="2">
<ElDescriptions border :column="2">
<ElDescriptionsItem label="编号">{{ detail.id }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户端编号">
{{ detail.clientMessageId || '-' }}

View File

@ -33,7 +33,7 @@ defineExpose({ open });
<template>
<ElDialog v-model="visible" title="私聊消息详情" width="700px">
<ElDescriptions bordered :column="2">
<ElDescriptions border :column="2">
<ElDescriptionsItem label="编号">{{ detail.id }}</ElDescriptionsItem>
<ElDescriptionsItem label="客户端编号">
{{ detail.clientMessageId || '-' }}

View File

@ -42,7 +42,7 @@ defineExpose({ open });
<template>
<ElDrawer v-model="visible" destroy-on-close title="通话记录详情" width="900">
<ElDescriptions bordered :column="2">
<ElDescriptions border :column="2">
<ElDescriptionsItem label="编号">{{ detail.id }}</ElDescriptionsItem>
<ElDescriptionsItem label="业务通话编号">{{ detail.room }}</ElDescriptionsItem>
<ElDescriptionsItem label="发起人">
@ -84,7 +84,7 @@ defineExpose({ open });
<ElTable
v-loading="loading"
:data="participants"
bordered
border
row-key="id"
size="small"
>