feat: support media account unbind requests
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
ExclamationCircleOutlined,
|
||||
GlobalOutlined,
|
||||
LinkOutlined,
|
||||
LockOutlined,
|
||||
MoreOutlined,
|
||||
PlusOutlined,
|
||||
ReloadOutlined,
|
||||
@@ -107,6 +108,19 @@ const activeMediaScope = ref<MediaScope>('ugc')
|
||||
const searchQuery = ref('')
|
||||
const selectedHealth = ref<AccountHealthFilter>('all')
|
||||
const downloadingReleaseKey = ref<string | null>(null)
|
||||
const requestingAccountDeleteId = ref<string | null>(null)
|
||||
const unbindModalOpen = ref(false)
|
||||
const unbindAccountTarget = ref<MediaAccountCard | null>(null)
|
||||
const unbindVerifyInput = ref('')
|
||||
const unbindConfirmLoading = ref(false)
|
||||
|
||||
const isUnbindVerified = computed(() => {
|
||||
if (!unbindAccountTarget.value) return false
|
||||
const targetName = unbindAccountTarget.value.displayName.trim()
|
||||
const inputVal = unbindVerifyInput.value.trim()
|
||||
return inputVal === targetName || inputVal === '解绑'
|
||||
})
|
||||
|
||||
const enterpriseSiteModalOpen = ref(false)
|
||||
const editingEnterpriseSiteId = ref<number | null>(null)
|
||||
const deletingEnterpriseSiteId = ref<number | null>(null)
|
||||
@@ -212,6 +226,19 @@ const deleteEnterpriseSiteMutation = useMutation({
|
||||
},
|
||||
})
|
||||
|
||||
const requestAccountDeleteMutation = useMutation({
|
||||
mutationFn: (payload: { id: string; undo?: boolean }) =>
|
||||
tenantAccountsApi.requestDelete(payload.id, { undo: payload.undo }),
|
||||
onSuccess: async (_account, payload) => {
|
||||
message.success(payload.undo ? '已撤销解绑申请' : '已申请解绑,客户端会自动清理本地授权')
|
||||
await accountsQuery.refetch()
|
||||
},
|
||||
onError: (error) => message.error(formatError(error)),
|
||||
onSettled: () => {
|
||||
requestingAccountDeleteId.value = null
|
||||
},
|
||||
})
|
||||
|
||||
const loading = computed(() => platformsQuery.isPending.value || accountsQuery.isPending.value)
|
||||
const refreshing = computed(
|
||||
() =>
|
||||
@@ -555,7 +582,7 @@ function handleEnterpriseSiteCMSTypeChange(value: CmsType): void {
|
||||
}
|
||||
|
||||
function getEnterpriseSiteSelectPopupContainer(triggerNode: HTMLElement): HTMLElement {
|
||||
return triggerNode.closest('.ant-modal-content') as HTMLElement || document.body
|
||||
return (triggerNode.closest('.ant-modal-content') as HTMLElement) || document.body
|
||||
}
|
||||
|
||||
function normalizeSiteUrl(value: string): string {
|
||||
@@ -663,6 +690,39 @@ function deleteEnterpriseSite(site: EnterpriseSiteConnection): void {
|
||||
})
|
||||
}
|
||||
|
||||
function requestUnbindAccount(account: MediaAccountCard): void {
|
||||
unbindAccountTarget.value = account
|
||||
unbindVerifyInput.value = ''
|
||||
unbindConfirmLoading.value = false
|
||||
unbindModalOpen.value = true
|
||||
}
|
||||
|
||||
async function handleConfirmUnbind(): Promise<void> {
|
||||
if (!isUnbindVerified.value || !unbindAccountTarget.value) return
|
||||
|
||||
unbindConfirmLoading.value = true
|
||||
try {
|
||||
requestingAccountDeleteId.value = unbindAccountTarget.value.id
|
||||
await requestAccountDeleteMutation.mutateAsync({ id: unbindAccountTarget.value.id })
|
||||
unbindModalOpen.value = false
|
||||
} catch (err) {
|
||||
// Error is handled by requestAccountDeleteMutation onError
|
||||
} finally {
|
||||
unbindConfirmLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function undoUnbindAccount(account: MediaAccountCard): void {
|
||||
requestingAccountDeleteId.value = account.id
|
||||
requestAccountDeleteMutation.mutate({ id: account.id, undo: true })
|
||||
}
|
||||
|
||||
function isAccountDeleteRequestPending(accountId: string): boolean {
|
||||
return (
|
||||
requestAccountDeleteMutation.isPending.value && requestingAccountDeleteId.value === accountId
|
||||
)
|
||||
}
|
||||
|
||||
function enterpriseSiteFaviconURL(site: EnterpriseSiteConnection): string {
|
||||
try {
|
||||
const parsed = new URL(site.site_url)
|
||||
@@ -939,7 +999,10 @@ function releaseSize(value?: number | null): string {
|
||||
<PlusOutlined class="empty-plus" />
|
||||
</div>
|
||||
<h3>暂无企业自建站点</h3>
|
||||
<p>对接您的 CMS 站点(如 PBootCMS、WordPress 等)后,系统将能够通过 API 自动将文章推送到您的自建网站,实现多渠道一键发布与同步。</p>
|
||||
<p>
|
||||
对接您的 CMS 站点(如 PBootCMS、WordPress 等)后,系统将能够通过 API
|
||||
自动将文章推送到您的自建网站,实现多渠道一键发布与同步。
|
||||
</p>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="large"
|
||||
@@ -955,7 +1018,7 @@ function releaseSize(value?: number | null): string {
|
||||
<article v-for="site in enterpriseSites" :key="site.id" class="site-connection-card">
|
||||
<!-- Top accent based on status -->
|
||||
<div class="site-card-accent" :class="`accent-${site.status || 'pending'}`"></div>
|
||||
|
||||
|
||||
<div class="site-connection-card__header">
|
||||
<span class="site-connection-card__icon">
|
||||
<img
|
||||
@@ -967,7 +1030,7 @@ function releaseSize(value?: number | null): string {
|
||||
/>
|
||||
<GlobalOutlined v-else />
|
||||
</span>
|
||||
|
||||
|
||||
<div class="site-connection-card__header-main">
|
||||
<div class="site-name-row">
|
||||
<h4>{{ site.name }}</h4>
|
||||
@@ -975,7 +1038,7 @@ function releaseSize(value?: number | null): string {
|
||||
{{ cmsTypeLabel(site.cms_type as CmsType) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
<span
|
||||
class="site-badge site-badge--status"
|
||||
:class="`site-badge--status-${site.status || 'pending'}`"
|
||||
@@ -993,7 +1056,7 @@ function releaseSize(value?: number | null): string {
|
||||
<span>{{ site.site_url }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="site-meta-grid">
|
||||
<div class="meta-cell">
|
||||
<span class="meta-label">{{ enterpriseSiteCredentialLabel(site) }}</span>
|
||||
@@ -1039,7 +1102,7 @@ function releaseSize(value?: number | null): string {
|
||||
<template #icon><SyncOutlined /></template>
|
||||
同步栏目
|
||||
</a-button>
|
||||
|
||||
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-button size="middle" class="action-more-btn" type="text" @click.stop>
|
||||
<template #icon><MoreOutlined /></template>
|
||||
@@ -1290,6 +1353,38 @@ function releaseSize(value?: number | null): string {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" overlayClassName="premium-unbind-dropdown">
|
||||
<a-button
|
||||
type="text"
|
||||
class="media-card__more action-more-btn"
|
||||
:loading="isAccountDeleteRequestPending(account.id)"
|
||||
@click.stop
|
||||
>
|
||||
<template #icon><MoreOutlined /></template>
|
||||
</a-button>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item
|
||||
v-if="account.deleteRequestedAt"
|
||||
key="undo-unbind"
|
||||
@click="undoUnbindAccount(account)"
|
||||
>
|
||||
<SyncOutlined />
|
||||
<span>撤销解绑申请</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item
|
||||
v-else
|
||||
key="unbind"
|
||||
danger
|
||||
@click="requestUnbindAccount(account)"
|
||||
>
|
||||
<DeleteOutlined />
|
||||
<span>解绑账号</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="media-card__tags">
|
||||
@@ -1365,6 +1460,120 @@ function releaseSize(value?: number | null): string {
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<!-- Custom Account Unbind Modal -->
|
||||
<a-modal
|
||||
v-model:open="unbindModalOpen"
|
||||
:destroy-on-close="true"
|
||||
:footer="null"
|
||||
:width="480"
|
||||
class="premium-unbind-modal"
|
||||
:closable="true"
|
||||
>
|
||||
<div class="unbind-modal-header">
|
||||
<div class="unbind-warning-icon-wrapper">
|
||||
<DeleteOutlined class="unbind-warning-icon" />
|
||||
</div>
|
||||
<h3 class="unbind-modal-title">申请解绑媒体账号</h3>
|
||||
<p class="unbind-modal-subtitle">请仔细阅读以下安全提示,解绑后可重新绑定。</p>
|
||||
</div>
|
||||
|
||||
<div class="unbind-account-preview" v-if="unbindAccountTarget">
|
||||
<div class="preview-avatar-wrapper">
|
||||
<img
|
||||
v-if="unbindAccountTarget.avatarUrl"
|
||||
:src="unbindAccountTarget.avatarUrl"
|
||||
class="preview-avatar"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="preview-avatar-placeholder"
|
||||
:style="{ background: unbindAccountTarget.platformAccent }"
|
||||
>
|
||||
{{ accountInitial(unbindAccountTarget) }}
|
||||
</span>
|
||||
<img
|
||||
v-if="unbindAccountTarget.platformLogoUrl"
|
||||
:src="unbindAccountTarget.platformLogoUrl"
|
||||
class="preview-platform-icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="preview-info">
|
||||
<span class="preview-name">{{ unbindAccountTarget.displayName }}</span>
|
||||
<span class="preview-meta">
|
||||
{{ unbindAccountTarget.platformLabel }} · ID: {{ unbindAccountTarget.platformUid }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="preview-status">
|
||||
<span
|
||||
class="badge-status"
|
||||
:class="`badge-status--${unbindAccountTarget.clientOnline ? 'success' : 'warning'}`"
|
||||
>
|
||||
<span class="indicator-dot"></span>
|
||||
{{ unbindAccountTarget.clientOnline ? '在线' : '离线' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unbind-consequences" v-if="unbindAccountTarget">
|
||||
<div class="consequence-item">
|
||||
<div class="consequence-icon-wrapper">
|
||||
<ExclamationCircleOutlined />
|
||||
</div>
|
||||
<div class="consequence-text">
|
||||
<strong>解除服务绑定</strong>:系统服务端将彻底清除此账号的授权同步及分发配置。
|
||||
</div>
|
||||
</div>
|
||||
<div class="consequence-item">
|
||||
<div class="consequence-icon-wrapper">
|
||||
<LockOutlined />
|
||||
</div>
|
||||
<div class="consequence-text">
|
||||
<strong>清理会话缓存</strong>:
|
||||
<span v-if="unbindAccountTarget.clientOnline">
|
||||
在线客户端将即时收到通知并清理所有登录会话 Cookie。
|
||||
</span>
|
||||
<span v-else>
|
||||
客户端不在线,系统已将解绑申请加入队列。客户端上线后将自动同步清理本地会话。
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="consequence-item">
|
||||
<div class="consequence-icon-wrapper">
|
||||
<CloseCircleOutlined />
|
||||
</div>
|
||||
<div class="consequence-text">
|
||||
<strong>分发任务失效</strong>:该账号将无法接收并执行任何发布任务,再次使用必须重新扫码登录授权。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="unbind-verification" v-if="unbindAccountTarget">
|
||||
<div class="verification-label">
|
||||
请输入账号名称 <span class="highlight-name">{{ unbindAccountTarget.displayName }}</span> 或输入 <span class="highlight-keyword">解绑</span> 进行确认:
|
||||
</div>
|
||||
<a-input
|
||||
v-model:value="unbindVerifyInput"
|
||||
placeholder="请输入名称或“解绑”"
|
||||
class="unbind-input"
|
||||
@pressEnter="handleConfirmUnbind"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="unbind-modal-footer">
|
||||
<a-button @click="unbindModalOpen = false" class="btn-cancel">取消</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
danger
|
||||
:disabled="!isUnbindVerified"
|
||||
:loading="unbindConfirmLoading"
|
||||
@click="handleConfirmUnbind"
|
||||
class="btn-confirm"
|
||||
>
|
||||
确认解绑
|
||||
</a-button>
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
<a-modal
|
||||
v-model:open="enterpriseSiteModalOpen"
|
||||
:title="enterpriseSiteModalTitle"
|
||||
@@ -2203,12 +2412,16 @@ function releaseSize(value?: number | null): string {
|
||||
padding: 24px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px -1px rgba(0, 0, 0, 0.01);
|
||||
box-shadow:
|
||||
0 4px 6px -1px rgba(0, 0, 0, 0.02),
|
||||
0 2px 4px -1px rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.site-connection-card:hover {
|
||||
border-color: #cbd5e1;
|
||||
box-shadow: 0 20px 25px -5px rgba(15, 23, 42, 0.08), 0 10px 10px -5px rgba(15, 23, 42, 0.04);
|
||||
box-shadow:
|
||||
0 20px 25px -5px rgba(15, 23, 42, 0.08),
|
||||
0 10px 10px -5px rgba(15, 23, 42, 0.04);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
@@ -2795,6 +3008,8 @@ function releaseSize(value?: number | null): string {
|
||||
.media-card__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.media-card__identity {
|
||||
@@ -2802,7 +3017,24 @@ function releaseSize(value?: number | null): string {
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.media-card__more.ant-btn-text {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 8px;
|
||||
color: #94a3b8;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.media-card__more.ant-btn-text:hover,
|
||||
.media-card__more.ant-btn-text:focus {
|
||||
color: #334155;
|
||||
background: #f8fafc;
|
||||
border-color: #e2e8f0;
|
||||
}
|
||||
|
||||
/* Circular Avatar glow container */
|
||||
@@ -3236,32 +3468,32 @@ function releaseSize(value?: number | null): string {
|
||||
.site-connection-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
|
||||
.site-meta-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
.meta-cell.full-width {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
|
||||
.site-connection-card__footer-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
.btn-action-ping.ant-btn {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.footer-actions-right {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.btn-action-sync.ant-btn {
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
@@ -3304,4 +3536,305 @@ function releaseSize(value?: number | null): string {
|
||||
:deep(.ant-dropdown-menu-item-danger:hover .anticon) {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Custom Account Unbind Modal Styles */
|
||||
.premium-unbind-modal :deep(.ant-modal-content) {
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.premium-unbind-modal :deep(.ant-modal-header) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.unbind-modal-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.unbind-warning-icon-wrapper {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
background-color: #fef2f2;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 16px;
|
||||
border: 1px solid #fee2e2;
|
||||
animation: pulse-warning 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes pulse-warning {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.15);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 0 10px rgba(239, 68, 68, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.unbind-warning-icon {
|
||||
font-size: 24px;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.unbind-modal-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.unbind-modal-subtitle {
|
||||
font-size: 13px;
|
||||
color: #64748b;
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Account Preview inside Modal */
|
||||
.unbind-account-preview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.preview-avatar-wrapper {
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.preview-avatar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 1px solid #cbd5e1;
|
||||
}
|
||||
|
||||
.preview-avatar-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.preview-platform-icon {
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
right: -2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
padding: 2px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.preview-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.preview-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.preview-meta {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.preview-status {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Consequences List */
|
||||
.unbind-consequences {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
background-color: #fffaf0;
|
||||
border: 1px dashed #feebc8;
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.consequence-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.consequence-icon-wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
color: #dd6b20;
|
||||
}
|
||||
|
||||
.consequence-icon-wrapper .anticon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.consequence-text {
|
||||
font-size: 13px;
|
||||
color: #7b341e;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.consequence-text strong {
|
||||
color: #dd6b20;
|
||||
}
|
||||
|
||||
/* Verification Input */
|
||||
.unbind-verification {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.verification-label {
|
||||
font-size: 13px;
|
||||
color: #475569;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.highlight-name {
|
||||
font-family: monospace;
|
||||
font-weight: 700;
|
||||
color: #ef4444;
|
||||
background-color: #fef2f2;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #fee2e2;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.highlight-keyword {
|
||||
font-weight: 700;
|
||||
color: #3b82f6;
|
||||
background-color: #eff6ff;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dbeafe;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.unbind-input.ant-input {
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
border-color: #cbd5e1;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.unbind-input.ant-input:focus {
|
||||
border-color: #ef4444;
|
||||
box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12);
|
||||
}
|
||||
|
||||
/* Footer Actions */
|
||||
.unbind-modal-footer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.unbind-modal-footer .btn-cancel {
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 0 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #475569;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.unbind-modal-footer .btn-cancel:hover {
|
||||
color: #1e293b;
|
||||
border-color: #94a3b8;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.unbind-modal-footer .btn-confirm {
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
padding: 0 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Premium Dropdown styles */
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu) {
|
||||
border-radius: 12px !important;
|
||||
padding: 6px !important;
|
||||
border: 1px solid #f1f5f9 !important;
|
||||
box-shadow: 0 10px 25px -5px rgba(15, 23, 42, 0.08), 0 8px 16px -6px rgba(15, 23, 42, 0.04) !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item) {
|
||||
border-radius: 8px !important;
|
||||
padding: 8px 16px !important;
|
||||
font-size: 13.5px !important;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item:not(.ant-dropdown-menu-item-danger):hover) {
|
||||
background-color: #eff6ff !important;
|
||||
color: #2563eb !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item:not(.ant-dropdown-menu-item-danger):hover .anticon) {
|
||||
color: #2563eb !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item-danger) {
|
||||
color: #ef4444 !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item-danger .anticon) {
|
||||
color: #ef4444 !important;
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item-danger:hover) {
|
||||
background-color: #fef2f2 !important;
|
||||
color: #dc2626 !important;
|
||||
}
|
||||
|
||||
:global(.premium-unbind-dropdown .ant-dropdown-menu-item-danger:hover .anticon) {
|
||||
color: #dc2626 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user