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