fix(frontend): polish integration channel card actions

Align add-card height with channel cards, show actions on hover,
and replace delete dialogs with in-place popconfirm like model settings.
This commit is contained in:
wizardchen
2026-06-24 19:35:15 +08:00
committed by lyingbug
parent 635d7c9643
commit ec5c3ac638
3 changed files with 81 additions and 48 deletions
@@ -45,12 +45,32 @@
variant="text"
shape="square"
size="small"
class="channel-card__more"
class="channel-card__action-btn channel-card__more"
@click.stop
>
<template #icon><t-icon name="ellipsis" /></template>
</t-button>
</t-dropdown>
<t-popconfirm
:content="$t('embedPublish.deleteConfirm')"
:confirm-btn="{ content: $t('common.delete'), theme: 'danger' }"
:cancel-btn="{ content: $t('common.cancel') }"
placement="bottom-right"
@confirm="() => removeChannel(ch.id)"
>
<t-tooltip :content="$t('common.delete')" placement="top">
<t-button
theme="danger"
shape="square"
variant="text"
size="small"
class="channel-card__action-btn channel-card__delete"
@click.stop
>
<template #icon><t-icon name="delete" /></template>
</t-button>
</t-tooltip>
</t-popconfirm>
</div>
</button>
@@ -524,7 +544,7 @@
<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next'
import { MessagePlugin } from 'tdesign-vue-next'
import { useAuthStore } from '@/stores/auth'
import SettingDrawer from '@/components/settings/SettingDrawer.vue'
import EmbedChannelPreview from '@/components/EmbedChannelPreview.vue'
@@ -680,7 +700,7 @@ const defaultLocaleOptions = computed(() => ([
]))
const channelMenuOptions = (ch: EmbedChannel) => {
const items: Array<{ content: string; value: string; theme?: 'error' }> = []
const items: Array<{ content: string; value: string }> = []
if (isAdmin.value) {
items.push({ content: t('embedPublish.preview'), value: 'preview' })
}
@@ -688,9 +708,6 @@ const channelMenuOptions = (ch: EmbedChannel) => {
content: ch.enabled ? t('common.off') : t('common.on'),
value: 'toggle',
})
if (isAdmin.value) {
items.push({ content: t('common.delete'), value: 'delete', theme: 'error' })
}
return items
}
@@ -701,10 +718,6 @@ function handleChannelMenuClick(data: { value?: string }, ch: EmbedChannel) {
}
if (data.value === 'toggle') {
void toggleEnabled(ch, !ch.enabled)
return
}
if (data.value === 'delete') {
confirmRemoveChannel(ch.id)
}
}
@@ -1216,20 +1229,6 @@ const performRotate = async (id: string) => {
}
}
const confirmRemoveChannel = (id: string) => {
const dialog = DialogPlugin.confirm({
header: t('common.delete'),
body: t('embedPublish.deleteConfirm'),
confirmBtn: { content: t('common.delete'), theme: 'danger' },
cancelBtn: t('common.cancel'),
onConfirm: async () => {
dialog.destroy()
await removeChannel(id)
},
onClose: () => dialog.destroy(),
})
}
const removeChannel = async (id: string) => {
await deleteEmbedChannel(id)
const map = loadStoredTokens()
+22 -21
View File
@@ -51,12 +51,32 @@
variant="text"
shape="square"
size="small"
class="channel-card__more"
class="channel-card__action-btn channel-card__more"
@click.stop
>
<template #icon><t-icon name="ellipsis" /></template>
</t-button>
</t-dropdown>
<t-popconfirm
:content="$t('agentEditor.im.deleteConfirm')"
:confirm-btn="{ content: $t('common.delete'), theme: 'danger' }"
:cancel-btn="{ content: $t('common.cancel') }"
placement="bottom-right"
@confirm="() => handleDelete(channel.id)"
>
<t-tooltip :content="$t('common.delete')" placement="top">
<t-button
theme="danger"
shape="square"
variant="text"
size="small"
class="channel-card__action-btn channel-card__delete"
@click.stop
>
<template #icon><t-icon name="delete" /></template>
</t-button>
</t-tooltip>
</t-popconfirm>
</div>
</button>
@@ -565,7 +585,7 @@
<script setup lang="ts">
import { ref, onMounted, watch, onUnmounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import { DialogPlugin, MessagePlugin } from 'tdesign-vue-next';
import { MessagePlugin } from 'tdesign-vue-next';
import {
listIMChannels, createIMChannel, updateIMChannel, deleteIMChannel, toggleIMChannel,
getWeChatQRCode, pollWeChatQRCodeStatus, listAllIMChannels, listAgents,
@@ -703,7 +723,6 @@ const channelMenuOptions = (channel: IMChannel | IMChannelOverview) => ([
content: channel.enabled ? t('common.off') : t('common.on'),
value: 'toggle',
},
{ content: t('common.delete'), value: 'delete', theme: 'error' as const },
]);
function handleChannelMenuClick(
@@ -712,10 +731,6 @@ function handleChannelMenuClick(
) {
if (data.value === 'toggle') {
void handleToggle(channel);
return;
}
if (data.value === 'delete') {
confirmDelete(channel.id);
}
}
@@ -935,20 +950,6 @@ function openDrawer(channel: IMChannel | IMChannelOverview) {
void editChannel(channel);
}
function confirmDelete(id: string) {
const dialog = DialogPlugin.confirm({
header: t('common.delete'),
body: t('agentEditor.im.deleteConfirm'),
confirmBtn: { content: t('common.delete'), theme: 'danger' },
cancelBtn: t('common.cancel'),
onConfirm: async () => {
dialog.destroy();
await handleDelete(id);
},
onClose: () => dialog.destroy(),
});
}
async function editChannel(channel: IMChannel | IMChannelOverview) {
wizardStep.value = 0;
let fullChannel: IMChannel | null = null;
@@ -41,6 +41,8 @@
gap: 12px;
height: 100%;
box-sizing: border-box;
// Match two-line body (title + agent name) so the add card aligns with existing channels.
min-height: calc(20px + 14px * 1.4 + 4px + 12px * 1.4);
padding: 10px 12px;
border: 1px solid var(--td-component-stroke);
border-radius: 10px;
@@ -54,6 +56,12 @@
cursor: pointer;
width: 100%;
.channel-card__actions:not(.channel-card__actions--spacer) {
opacity: 0;
pointer-events: none;
transition: opacity 0.15s ease;
}
&:hover,
&:focus-visible {
border-color: var(--td-brand-color-3, var(--td-brand-color));
@@ -61,12 +69,25 @@
outline: none;
}
&:hover .channel-card__actions:not(.channel-card__actions--spacer),
&:focus-within .channel-card__actions:not(.channel-card__actions--spacer) {
opacity: 1;
pointer-events: auto;
}
&:focus-visible {
outline: 2px solid var(--td-brand-color);
outline-offset: 2px;
}
}
@media (hover: none) {
&--clickable .channel-card__actions:not(.channel-card__actions--spacer) {
opacity: 1;
pointer-events: auto;
}
}
&--add {
align-items: center;
justify-content: flex-start;
@@ -160,16 +181,18 @@
margin-left: auto;
&--spacer {
width: 24px;
width: 52px;
height: 24px;
}
}
&__more {
&__action-btn {
flex-shrink: 0;
padding: 2px;
}
&__more {
color: var(--td-text-color-placeholder);
transition: color 0.15s ease, background 0.15s ease;
&:hover,
&:focus-visible {
@@ -177,6 +200,16 @@
color: var(--td-text-color-primary);
}
}
&__delete {
color: var(--td-error-color);
&:hover,
&:focus-visible {
background: color-mix(in srgb, var(--td-error-color) 12%, transparent);
color: var(--td-error-color);
}
}
}
.agent-field-row {