优化平台配置导航布局

This commit is contained in:
yml2213
2026-05-26 09:39:59 +08:00
parent 5c7e145766
commit 8b1904d732
4 changed files with 313 additions and 221 deletions
+1
View File
@@ -45,6 +45,7 @@ declare module 'vue' {
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
@@ -2,6 +2,8 @@
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {
Bell,
Connection,
Monitor,
DataAnalysis,
Document,
@@ -27,6 +29,7 @@ import { formatAdminDateTime } from '@/utils/admin-time'
const route = useRoute()
const router = useRouter()
const sidebarStorageKey = 'admin-sidebar-collapsed'
const isDevMode = import.meta.env.DEV
const isSidebarCollapsed = ref(
typeof window !== 'undefined' && window.localStorage.getItem(sidebarStorageKey) === '1',
)
@@ -40,8 +43,33 @@ const roleLabel = computed(() => {
return '普通运营'
})
type AdminNavItem = {
to: string
label: string
icon: typeof DataAnalysis
children?: AdminNavItem[]
}
const platformConfigNavItems = computed<AdminNavItem[]>(() => {
const items: AdminNavItem[] = [
{ to: '/admin/platform-shops?tab=ninetyone', label: '91卡券', icon: Connection },
{ to: '/admin/platform-shops?tab=notifications', label: '内部通知', icon: Bell },
{ to: '/admin/platform-shops?tab=kuaishouEticket', label: '快手小店核销', icon: Tickets },
]
if (isDevMode) {
items.push({
to: '/admin/platform-shops?tab=cloudtentacles',
label: 'cloudtentacles',
icon: Setting,
})
}
return items
})
const navItems = computed(() => {
const items: { to: string; label: string; icon: typeof DataAnalysis }[] = [
const items: AdminNavItem[] = [
{ to: '/admin/dashboard', label: '概览', icon: DataAnalysis },
]
@@ -56,7 +84,12 @@ const navItems = computed(() => {
if (isAdmin.value) {
items.push(
{ to: '/admin/platform-shops', label: '平台配置', icon: Setting },
{
to: '/admin/platform-shops',
label: '平台配置',
icon: Setting,
children: platformConfigNavItems.value,
},
{ to: '/admin/platform-fulfillment', label: '履约配置', icon: Tickets },
{ to: '/admin/audit-logs', label: '审计', icon: Monitor },
)
@@ -65,6 +98,15 @@ const navItems = computed(() => {
return items
})
const activeNavIndex = computed(() => {
if (route.path === '/admin/platform-shops') {
const tab = typeof route.query.tab === 'string' ? route.query.tab : 'ninetyone'
return `/admin/platform-shops?tab=${tab}`
}
return route.path
})
function toggleSidebarCollapse() {
isSidebarCollapsed.value = !isSidebarCollapsed.value
if (typeof window !== 'undefined') {
@@ -112,17 +154,37 @@ async function submitLogout() {
</div>
<el-menu
:default-active="route.path"
:default-active="activeNavIndex"
:default-openeds="['/admin/platform-shops']"
router
class="sidebar-menu"
:collapse="isSidebarCollapsed"
:collapse-transition="false"
:ellipsis="false"
>
<el-menu-item v-for="item in navItems" :key="item.to" :index="item.to" :title="item.label">
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
<template v-for="item in navItems" :key="item.to">
<el-sub-menu v-if="item.children?.length" :index="item.to">
<template #title>
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</template>
<el-menu-item
v-for="child in item.children"
:key="child.to"
:index="child.to"
:title="child.label"
>
<el-icon><component :is="child.icon" /></el-icon>
<span>{{ child.label }}</span>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="item.to" :title="item.label">
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</el-menu-item>
</template>
</el-menu>
<el-tooltip :disabled="!isSidebarCollapsed" content="退出登录" placement="right">
@@ -235,18 +297,33 @@ async function submitLogout() {
width: 100%;
}
.sidebar-menu :deep(.el-menu-item) {
.sidebar-menu :deep(.el-menu-item),
.sidebar-menu :deep(.el-sub-menu__title) {
margin: 2px var(--space-2);
border-radius: var(--radius-md);
min-height: 44px;
line-height: 44px;
}
.sidebar-menu :deep(.el-menu-item.is-active) {
.sidebar-menu :deep(.el-menu-item.is-active),
.sidebar-menu :deep(.el-sub-menu.is-active > .el-sub-menu__title) {
color: var(--color-primary);
background: var(--color-primary-light);
}
.sidebar-menu :deep(.el-sub-menu .el-menu-item) {
margin-left: var(--space-4);
min-height: 38px;
line-height: 38px;
color: var(--text-secondary);
font-size: var(--text-sm);
}
.sidebar-menu :deep(.el-sub-menu .el-menu-item.is-active) {
color: var(--color-primary);
font-weight: 700;
}
.logout-btn {
margin: var(--space-3) var(--space-4) 0;
flex-shrink: 0;
@@ -263,13 +340,15 @@ async function submitLogout() {
margin-bottom: var(--space-5);
}
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item) {
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item),
.is-sidebar-collapsed .sidebar-menu :deep(.el-sub-menu__title) {
justify-content: center;
margin: 4px 10px;
padding: 0;
}
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item .el-icon) {
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item .el-icon),
.is-sidebar-collapsed .sidebar-menu :deep(.el-sub-menu__title .el-icon) {
margin-right: 0;
}
@@ -313,7 +392,8 @@ async function submitLogout() {
overflow: visible;
}
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item) {
.is-sidebar-collapsed .sidebar-menu :deep(.el-menu-item),
.is-sidebar-collapsed .sidebar-menu :deep(.el-sub-menu__title) {
justify-content: flex-start;
margin: 2px var(--space-2);
padding: 0 var(--space-5);
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
import { RefreshRight } from '@element-plus/icons-vue'
import AdminPageHeader from '@/components/admin/AdminPageHeader.vue'
@@ -23,7 +24,21 @@ import type { PlatformTab } from './composables/types'
const loading = ref(true)
const isDevMode = import.meta.env.DEV
const errorMessage = ref('')
const activePlatform = ref<PlatformTab>('ninetyone')
const route = useRoute()
const platformTabs: PlatformTab[] = ['ninetyone', 'notifications', 'kuaishouEticket']
if (isDevMode) {
platformTabs.push('cloudtentacles')
}
function normalizePlatformTab(value: unknown): PlatformTab {
return typeof value === 'string' && platformTabs.includes(value as PlatformTab)
? (value as PlatformTab)
: 'ninetyone'
}
const activePlatform = computed(() => normalizePlatformTab(route.query.tab))
const {
ninetyoneLoading,
@@ -147,42 +162,46 @@ const overviewCards = computed(() => {
{
key: 'notifications',
label: '内部通知',
tag: 'Bark / WPush',
tag: '通知与监控',
value:
notificationStats.value.barkEnabledRecipientCount +
notificationStats.value.wpushEnabledRecipientCount,
unit: '启用接收人',
meta: `Bark ${notificationStats.value.barkConfiguredRecipientCount} 人 · WPush ${notificationStats.value.wpushConfiguredRecipientCount} 人 · 最近成功 ${notificationStats.value.lastSuccessCount}`,
status: notificationStats.value.ready ? '已就绪' : '待配置',
},
{
key: 'ninetyone',
label: '91卡券',
tag: '接入 / 待补全',
tag: '来源订单',
value: ninetyoneStats.value.pendingCount,
unit: '待补全订单',
meta: `当前 ${ninetyoneStats.value.currentCount} 条 · 已生成任务 ${ninetyoneStats.value.taskReadyCount}`,
status: ninetyoneStats.value.pendingCount > 0 ? '待处理' : '正常',
},
{
key: 'kuaishouEticket',
label: '快手小店核销',
tag: 'Cookie / 券核销',
tag: '店铺凭据',
value: kuaishouEticketStats.value.configuredShopCount,
unit: '已配置店铺',
meta: `Cookie 就绪 ${kuaishouEticketStats.value.cookieReadyCount} 家 · 当前店铺 ${
kuaishouEticketStats.value.selectedShopReady ? '可调试' : '待补全'
}`,
status: kuaishouEticketStats.value.cookieReadyCount > 0 ? '已就绪' : '待配置',
},
...(isDevMode
? [
{
key: 'cloudtentacles',
label: 'cloudtentacles',
tag: '履约 / 登录',
tag: '履约账号',
value: cloudtentaclesStats.value.hasCredential ? '已配置' : '未配置',
unit: '履约凭据',
meta: `短信 ${cloudtentaclesStats.value.smsSent ? '已发送' : '未发送'} · 会话 ${
cloudtentaclesStats.value.validated ? '已验证' : '未验证'
}`,
status: cloudtentaclesStats.value.validated ? '已验证' : '待验证',
},
]
: []),
@@ -191,6 +210,11 @@ const overviewCards = computed(() => {
return cards
})
const activeOverview = computed(
() =>
overviewCards.value.find((card) => card.key === activePlatform.value) || overviewCards.value[0],
)
async function loadConfigs() {
if (!hasAdminRole('admin')) {
loading.value = false
@@ -225,10 +249,6 @@ async function loadConfigs() {
}
}
function switchTab(tab: string) {
activePlatform.value = tab as PlatformTab
}
onMounted(loadConfigs)
</script>
@@ -267,150 +287,139 @@ onMounted(loadConfigs)
<el-skeleton v-if="loading" :rows="8" animated />
<template v-else>
<!-- 概览卡片 -->
<div class="overview-grid">
<el-card
v-for="card in overviewCards"
:key="card.key"
:class="['overview-card', { 'is-active': activePlatform === card.key }]"
shadow="hover"
@click="switchTab(card.key)"
>
<div class="card-head">
<el-tag size="small" type="primary">{{ card.label }}</el-tag>
<el-tag size="small">{{ card.tag }}</el-tag>
<main class="platform-detail">
<section v-if="activeOverview" class="active-summary">
<div class="active-summary-copy">
<div class="summary-kicker">{{ activeOverview.tag }}</div>
<h2>{{ activeOverview.label }}</h2>
<p>{{ activeOverview.meta }}</p>
</div>
<div class="card-value">{{ card.value }}</div>
<div class="card-unit">{{ card.unit }}</div>
<div class="card-meta">{{ card.meta }}</div>
</el-card>
</div>
<div class="summary-metrics">
<div class="summary-metric">
<span>{{ activeOverview.unit }}</span>
<strong>{{ activeOverview.value }}</strong>
</div>
<el-tag size="large" effect="plain">{{ activeOverview.status }}</el-tag>
</div>
</section>
<!-- 平台切换 Tab -->
<el-tabs v-model="activePlatform" type="card" class="platform-tabs">
<el-tab-pane label="91卡券接入" name="ninetyone">
<AdminPlatformNinetyoneSection
:ninetyone-loading="ninetyoneLoading"
:ninetyone-action-loading-id="ninetyoneActionLoadingId"
:ninetyone-result-error="ninetyoneResultError"
:ninetyone-status="ninetyoneStatus"
:ninetyone-orders="ninetyoneOrders"
:ninetyone-stats="ninetyoneStats"
:load-ninetyone-orders="loadNinetyoneOrders"
:handle-ninetyone-status-change="handleNinetyoneStatusChange"
:handle-ninetyone-retry-order="handleNinetyoneRetryOrder"
:handle-ninetyone-fail-order="handleNinetyoneFailOrder"
/>
</el-tab-pane>
<AdminPlatformNinetyoneSection
v-if="activePlatform === 'ninetyone'"
:ninetyone-loading="ninetyoneLoading"
:ninetyone-action-loading-id="ninetyoneActionLoadingId"
:ninetyone-result-error="ninetyoneResultError"
:ninetyone-status="ninetyoneStatus"
:ninetyone-orders="ninetyoneOrders"
:ninetyone-stats="ninetyoneStats"
:load-ninetyone-orders="loadNinetyoneOrders"
:handle-ninetyone-status-change="handleNinetyoneStatusChange"
:handle-ninetyone-retry-order="handleNinetyoneRetryOrder"
:handle-ninetyone-fail-order="handleNinetyoneFailOrder"
/>
<el-tab-pane label="内部通知" name="notifications">
<AdminPlatformNotificationSection
:notification-file-path="notificationFilePath"
:notification-form="notificationForm"
:notification-recipients="notificationRecipients"
:wpush-recipients="wpushRecipients"
:notification-saving="notificationSaving"
:notification-testing="notificationTesting"
:notification-result-error="notificationResultError"
:notification-test-result="notificationTestResult"
:scheduled-jobs-file-path="scheduledJobsFilePath"
:scheduled-jobs-form="scheduledJobsForm"
:scheduled-jobs="scheduledJobs"
:scheduled-job-runtime="scheduledJobRuntime"
:cloudtentacles-monitor-accounts="cloudtentaclesMonitorAccounts"
:scheduled-jobs-saving="scheduledJobsSaving"
:scheduled-job-running-id="scheduledJobRunningId"
:notification-stats="notificationStats"
:add-notification-recipient="addNotificationRecipient"
:remove-notification-recipient="removeNotificationRecipient"
:handle-notification-save-config="handleNotificationSaveConfig"
:handle-notification-test="handleNotificationTest"
:handle-scheduled-jobs-save-config="handleScheduledJobsSaveConfig"
:handle-scheduled-job-run-now="handleScheduledJobRunNow"
:get-scheduled-job-runtime="getScheduledJobRuntime"
/>
</el-tab-pane>
<AdminPlatformNotificationSection
v-else-if="activePlatform === 'notifications'"
:notification-file-path="notificationFilePath"
:notification-form="notificationForm"
:notification-recipients="notificationRecipients"
:wpush-recipients="wpushRecipients"
:notification-saving="notificationSaving"
:notification-testing="notificationTesting"
:notification-result-error="notificationResultError"
:notification-test-result="notificationTestResult"
:scheduled-jobs-file-path="scheduledJobsFilePath"
:scheduled-jobs-form="scheduledJobsForm"
:scheduled-jobs="scheduledJobs"
:scheduled-job-runtime="scheduledJobRuntime"
:cloudtentacles-monitor-accounts="cloudtentaclesMonitorAccounts"
:scheduled-jobs-saving="scheduledJobsSaving"
:scheduled-job-running-id="scheduledJobRunningId"
:notification-stats="notificationStats"
:add-notification-recipient="addNotificationRecipient"
:remove-notification-recipient="removeNotificationRecipient"
:handle-notification-save-config="handleNotificationSaveConfig"
:handle-notification-test="handleNotificationTest"
:handle-scheduled-jobs-save-config="handleScheduledJobsSaveConfig"
:handle-scheduled-job-run-now="handleScheduledJobRunNow"
:get-scheduled-job-runtime="getScheduledJobRuntime"
/>
<el-tab-pane label="快手小店核销" name="kuaishouEticket">
<AdminPlatformKuaishouEticketSection
:kuaishou-eticket-file-path="kuaishouEticketFilePath"
:kuaishou-eticket-shops="kuaishouEticketShops"
:kuaishou-eticket-form="kuaishouEticketForm"
:kuaishou-eticket-saving="kuaishouEticketSaving"
:kuaishou-eticket-resolving-shop-id="kuaishouEticketResolvingShopId"
:kuaishou-eticket-detailing="kuaishouEticketDetailing"
:kuaishou-eticket-consuming="kuaishouEticketConsuming"
:kuaishou-eticket-result-error="kuaishouEticketResultError"
:kuaishou-eticket-shop-info-result="kuaishouEticketShopInfoResult"
:kuaishou-eticket-detail-result="kuaishouEticketDetailResult"
:kuaishou-eticket-consume-result="kuaishouEticketConsumeResult"
:active-kuaishou-eticket-shop="activeKuaishouEticketShop"
:kuaishou-eticket-stats="kuaishouEticketStats"
:add-kuaishou-eticket-shop="addKuaishouEticketShop"
:remove-kuaishou-eticket-shop="removeKuaishouEticketShop"
:is-kuaishou-eticket-shop-expanded="isKuaishouEticketShopExpanded"
:toggle-kuaishou-eticket-shop="toggleKuaishouEticketShop"
:expand-all-kuaishou-eticket-shops="expandAllKuaishouEticketShops"
:collapse-all-kuaishou-eticket-shops="collapseAllKuaishouEticketShops"
:set-kuaishou-eticket-debug-shop="setKuaishouEticketDebugShop"
:describe-kuaishou-eticket-shop="describeKuaishouEticketShop"
:get-kuaishou-eticket-shop-initial="getKuaishouEticketShopInitial"
:handle-kuaishou-eticket-resolve-shop-info="handleKuaishouEticketResolveShopInfo"
:handle-kuaishou-eticket-save-source="handleKuaishouEticketSaveSource"
:handle-kuaishou-eticket-query-detail="handleKuaishouEticketQueryDetail"
:handle-kuaishou-eticket-consume="handleKuaishouEticketConsume"
/>
</el-tab-pane>
<AdminPlatformKuaishouEticketSection
v-else-if="activePlatform === 'kuaishouEticket'"
:kuaishou-eticket-file-path="kuaishouEticketFilePath"
:kuaishou-eticket-shops="kuaishouEticketShops"
:kuaishou-eticket-form="kuaishouEticketForm"
:kuaishou-eticket-saving="kuaishouEticketSaving"
:kuaishou-eticket-resolving-shop-id="kuaishouEticketResolvingShopId"
:kuaishou-eticket-detailing="kuaishouEticketDetailing"
:kuaishou-eticket-consuming="kuaishouEticketConsuming"
:kuaishou-eticket-result-error="kuaishouEticketResultError"
:kuaishou-eticket-shop-info-result="kuaishouEticketShopInfoResult"
:kuaishou-eticket-detail-result="kuaishouEticketDetailResult"
:kuaishou-eticket-consume-result="kuaishouEticketConsumeResult"
:active-kuaishou-eticket-shop="activeKuaishouEticketShop"
:kuaishou-eticket-stats="kuaishouEticketStats"
:add-kuaishou-eticket-shop="addKuaishouEticketShop"
:remove-kuaishou-eticket-shop="removeKuaishouEticketShop"
:is-kuaishou-eticket-shop-expanded="isKuaishouEticketShopExpanded"
:toggle-kuaishou-eticket-shop="toggleKuaishouEticketShop"
:expand-all-kuaishou-eticket-shops="expandAllKuaishouEticketShops"
:collapse-all-kuaishou-eticket-shops="collapseAllKuaishouEticketShops"
:set-kuaishou-eticket-debug-shop="setKuaishouEticketDebugShop"
:describe-kuaishou-eticket-shop="describeKuaishouEticketShop"
:get-kuaishou-eticket-shop-initial="getKuaishouEticketShopInitial"
:handle-kuaishou-eticket-resolve-shop-info="handleKuaishouEticketResolveShopInfo"
:handle-kuaishou-eticket-save-source="handleKuaishouEticketSaveSource"
:handle-kuaishou-eticket-query-detail="handleKuaishouEticketQueryDetail"
:handle-kuaishou-eticket-consume="handleKuaishouEticketConsume"
/>
<el-tab-pane v-if="isDevMode" label="cloudtentacles 履约平台" name="cloudtentacles">
<AdminPlatformCloudtentaclesSection
:cloudtentacles-file-path="cloudtentaclesFilePath"
:cloudtentacles-session-file-path="cloudtentaclesSessionFilePath"
:cloudtentacles-form="cloudtentaclesForm"
:cloudtentacles-saving="cloudtentaclesSaving"
:cloudtentacles-sending-sms="cloudtentaclesSendingSms"
:cloudtentacles-testing="cloudtentaclesTesting"
:cloudtentacles-validating="cloudtentaclesValidating"
:cloudtentacles-result-error="cloudtentaclesResultError"
:cloudtentacles-sms-result="cloudtentaclesSmsResult"
:cloudtentacles-login-result="cloudtentaclesLoginResult"
:cloudtentacles-validate-result="cloudtentaclesValidateResult"
:cloudtentacles-debug-loading="cloudtentaclesDebugLoading"
:cloudtentacles-debug-result="cloudtentaclesDebugResult"
:cloudtentacles-persisted-session="cloudtentaclesPersistedSession"
:cloudtentacles-stats="cloudtentaclesStats"
:sources="cloudtentaclesSources"
:sessions="cloudtentaclesSessions"
:selected-source-key="selectedCloudtentaclesSourceKey"
:current-source-enabled="currentCloudtentaclesSourceEnabled"
:current-source="currentCloudtentaclesSource"
:current-session="currentCloudtentaclesSession"
:switch-source="switchCloudtentaclesSource"
:add-source="addCloudtentaclesSource"
:remove-source="removeCloudtentaclesSource"
:handle-cloudtentacles-save-source="handleCloudtentaclesSaveSource"
:handle-cloudtentacles-send-sms-code="handleCloudtentaclesSendSmsCode"
:handle-cloudtentacles-test-login="handleCloudtentaclesTestLogin"
:handle-cloudtentacles-validate-session="handleCloudtentaclesValidateSession"
:handle-cloudtentacles-fetch-asset="handleCloudtentaclesFetchAsset"
:handle-cloudtentacles-fetch-categories="handleCloudtentaclesFetchCategories"
:handle-cloudtentacles-fetch-sku-list="handleCloudtentaclesFetchSkuList"
:handle-cloudtentacles-buy-sku="handleCloudtentaclesBuySku"
:handle-cloudtentacles-use-sku="handleCloudtentaclesUseSku"
:handle-cloudtentacles-fetch-knapsack="handleCloudtentaclesFetchKnapsack"
:handle-cloudtentacles-fetch-vn-list="handleCloudtentaclesFetchVnList"
:handle-cloudtentacles-appoint-vn="handleCloudtentaclesAppointVn"
:handle-cloudtentacles-generate-vn-login-code="
handleCloudtentaclesGenerateVnLoginCode
"
:handle-cloudtentacles-fetch-vn-code="handleCloudtentaclesFetchVnCode"
:handle-cloudtentacles-verify-vn-code="handleCloudtentaclesVerifyVnCode"
:handle-cloudtentacles-fetch-bind-url="handleCloudtentaclesFetchBindUrl"
:handle-cloudtentacles-back-vn="handleCloudtentaclesBackVn"
:handle-cloudtentacles-run-full-flow="handleCloudtentaclesRunFullFlow"
/>
</el-tab-pane>
</el-tabs>
<AdminPlatformCloudtentaclesSection
v-else-if="isDevMode && activePlatform === 'cloudtentacles'"
:cloudtentacles-file-path="cloudtentaclesFilePath"
:cloudtentacles-session-file-path="cloudtentaclesSessionFilePath"
:cloudtentacles-form="cloudtentaclesForm"
:cloudtentacles-saving="cloudtentaclesSaving"
:cloudtentacles-sending-sms="cloudtentaclesSendingSms"
:cloudtentacles-testing="cloudtentaclesTesting"
:cloudtentacles-validating="cloudtentaclesValidating"
:cloudtentacles-result-error="cloudtentaclesResultError"
:cloudtentacles-sms-result="cloudtentaclesSmsResult"
:cloudtentacles-login-result="cloudtentaclesLoginResult"
:cloudtentacles-validate-result="cloudtentaclesValidateResult"
:cloudtentacles-debug-loading="cloudtentaclesDebugLoading"
:cloudtentacles-debug-result="cloudtentaclesDebugResult"
:cloudtentacles-persisted-session="cloudtentaclesPersistedSession"
:cloudtentacles-stats="cloudtentaclesStats"
:sources="cloudtentaclesSources"
:sessions="cloudtentaclesSessions"
:selected-source-key="selectedCloudtentaclesSourceKey"
:current-source-enabled="currentCloudtentaclesSourceEnabled"
:current-source="currentCloudtentaclesSource"
:current-session="currentCloudtentaclesSession"
:switch-source="switchCloudtentaclesSource"
:add-source="addCloudtentaclesSource"
:remove-source="removeCloudtentaclesSource"
:handle-cloudtentacles-save-source="handleCloudtentaclesSaveSource"
:handle-cloudtentacles-send-sms-code="handleCloudtentaclesSendSmsCode"
:handle-cloudtentacles-test-login="handleCloudtentaclesTestLogin"
:handle-cloudtentacles-validate-session="handleCloudtentaclesValidateSession"
:handle-cloudtentacles-fetch-asset="handleCloudtentaclesFetchAsset"
:handle-cloudtentacles-fetch-categories="handleCloudtentaclesFetchCategories"
:handle-cloudtentacles-fetch-sku-list="handleCloudtentaclesFetchSkuList"
:handle-cloudtentacles-buy-sku="handleCloudtentaclesBuySku"
:handle-cloudtentacles-use-sku="handleCloudtentaclesUseSku"
:handle-cloudtentacles-fetch-knapsack="handleCloudtentaclesFetchKnapsack"
:handle-cloudtentacles-fetch-vn-list="handleCloudtentaclesFetchVnList"
:handle-cloudtentacles-appoint-vn="handleCloudtentaclesAppointVn"
:handle-cloudtentacles-generate-vn-login-code="handleCloudtentaclesGenerateVnLoginCode"
:handle-cloudtentacles-fetch-vn-code="handleCloudtentaclesFetchVnCode"
:handle-cloudtentacles-verify-vn-code="handleCloudtentaclesVerifyVnCode"
:handle-cloudtentacles-fetch-bind-url="handleCloudtentaclesFetchBindUrl"
:handle-cloudtentacles-back-vn="handleCloudtentaclesBackVn"
:handle-cloudtentacles-run-full-flow="handleCloudtentaclesRunFullFlow"
/>
</main>
</template>
</template>
</div>
@@ -422,68 +431,85 @@ onMounted(loadConfigs)
gap: var(--space-4);
}
/* 概览卡片网格 */
.overview-grid {
.platform-detail {
min-width: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
gap: var(--space-4);
}
.overview-card {
cursor: pointer;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease;
}
.overview-card :deep(.el-card__body) {
padding: 18px;
}
.overview-card.is-active {
border-color: var(--border-hover);
box-shadow: var(--shadow-lg);
}
.card-head {
.active-summary {
display: flex;
gap: var(--space-2);
margin-bottom: var(--space-3);
justify-content: space-between;
gap: var(--space-4);
align-items: center;
padding: var(--space-4) var(--space-5);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
background: rgba(255, 255, 255, 0.92);
box-shadow: var(--shadow-sm);
}
.card-value {
font-size: 26px;
font-weight: 700;
color: var(--text-primary);
line-height: 1.1;
.active-summary-copy {
min-width: 0;
}
.card-unit {
color: var(--text-muted);
font-size: var(--text-sm);
margin-top: var(--space-1);
}
.card-meta {
color: var(--text-secondary);
.summary-kicker {
color: #2563eb;
font-size: var(--text-xs);
margin-top: var(--space-2);
font-weight: 800;
}
/* Tab 切换 */
.platform-tabs {
margin-top: var(--space-2);
.active-summary h2 {
margin: 4px 0 0;
color: var(--text-primary);
font-size: var(--text-xl);
line-height: 1.25;
}
@media (max-width: 900px) {
.overview-grid {
grid-template-columns: repeat(2, 1fr);
.active-summary p {
margin: var(--space-2) 0 0;
color: var(--text-secondary);
font-size: var(--text-sm);
line-height: 1.6;
}
.summary-metrics {
flex: 0 0 auto;
display: flex;
gap: var(--space-3);
align-items: center;
}
.summary-metric {
min-width: 112px;
display: grid;
gap: 4px;
padding: 10px 12px;
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: var(--radius-sm);
background: #f8fafc;
}
.summary-metric span {
color: var(--text-muted);
font-size: var(--text-xs);
font-weight: 700;
}
.summary-metric strong {
color: var(--text-primary);
font-size: var(--text-xl);
line-height: 1;
}
@media (max-width: 720px) {
.active-summary {
align-items: stretch;
flex-direction: column;
}
}
@media (max-width: 640px) {
.overview-grid {
grid-template-columns: 1fr;
.summary-metrics {
justify-content: space-between;
}
}
</style>
@@ -28,21 +28,6 @@ defineProps<Props>()
<template>
<div class="platform-section">
<!-- 说明信息 -->
<el-card class="section-card">
<template #header>
<span class="card-title">说明信息</span>
</template>
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="职责">
接收 91卡券推送订单未命中履约配置时先进入待补全队列补完商品规则后可手动重试生成任务
</el-descriptions-item>
<el-descriptions-item label="处理建议">
先到"快手 Cloud 新履约"页为 91 productNo 配置规则再回到这里点击重试
</el-descriptions-item>
</el-descriptions>
</el-card>
<!-- 订单队列 -->
<el-card class="section-card">
<template #header>