补全一些基础信息

This commit is contained in:
yml2213
2026-05-13 20:18:58 +08:00
parent c2fd9078b9
commit a95e48b708
23 changed files with 1237 additions and 89 deletions
+2
View File
@@ -16,9 +16,11 @@ declare module 'vue' {
AdminPlatformCloudtentaclesSection: typeof import('./components/admin/AdminPlatformCloudtentaclesSection.vue')['default']
AdminPlatformKhhaoSection: typeof import('./components/admin/AdminPlatformKhhaoSection.vue')['default']
AdminPlatformKuaishouEticketSection: typeof import('./components/admin/AdminPlatformKuaishouEticketSection.vue')['default']
AdminPlatformNinetyoneSection: typeof import('./components/admin/AdminPlatformNinetyoneSection.vue')['default']
AdminResultCard: typeof import('./components/admin/AdminResultCard.vue')['default']
AdminStatusTag: typeof import('./components/admin/AdminStatusTag.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
@@ -0,0 +1,119 @@
<script setup lang="ts">
import type { AdminNinetyoneOrderItem, AdminNinetyoneOrderListResult } from '@/types/admin'
import { formatAdminDateTime } from '@/utils/admin-time'
type Props = {
ninetyoneLoading: boolean
ninetyoneActionLoadingId: number | null
ninetyoneResultError: string
ninetyoneStatus: 'pending_config' | 'all' | 'manual_failed'
ninetyoneOrders: AdminNinetyoneOrderListResult
ninetyoneStats: {
total: number
currentCount: number
pendingCount: number
failedCount: number
taskReadyCount: number
}
loadNinetyoneOrders: (page?: number) => void | Promise<void>
handleNinetyoneStatusChange: (status: 'pending_config' | 'all' | 'manual_failed') => void | Promise<void>
handleNinetyoneRetryOrder: (item: AdminNinetyoneOrderItem) => void | Promise<void>
handleNinetyoneFailOrder: (item: AdminNinetyoneOrderItem) => void | Promise<void>
}
defineProps<Props>()
</script>
<template>
<section class="meta-card">
<div class="meta-line">
<span class="meta-label">职责</span>
<span>接收 91卡券推送订单未命中履约配置时先进入待补全队列补完商品规则后可手动重试生成任务</span>
</div>
<div class="meta-line">
<span class="meta-label">处理建议</span>
<span>先到快手 Cloud 新履约页为 91 productNo 配置规则再回到这里点击重试</span>
</div>
</section>
<section class="table-card">
<div class="section-title-row">
<div>
<h3>91卡券订单队列</h3>
<p>当前筛选 {{ ninetyoneStats.currentCount }} 总计 {{ ninetyoneStats.total }} 已生成任务 {{ ninetyoneStats.taskReadyCount }} </p>
</div>
<div class="section-actions">
<el-button-group>
<el-button :type="ninetyoneStatus === 'pending_config' ? 'primary' : 'default'" round @click="handleNinetyoneStatusChange('pending_config')">待补全</el-button>
<el-button :type="ninetyoneStatus === 'manual_failed' ? 'primary' : 'default'" round @click="handleNinetyoneStatusChange('manual_failed')">失败</el-button>
<el-button :type="ninetyoneStatus === 'all' ? 'primary' : 'default'" round @click="handleNinetyoneStatusChange('all')">全部</el-button>
</el-button-group>
<el-button :loading="ninetyoneLoading" round @click="loadNinetyoneOrders(ninetyoneOrders.page)">刷新</el-button>
</div>
</div>
<p v-if="ninetyoneResultError" class="error-copy">{{ ninetyoneResultError }}</p>
<div v-if="ninetyoneLoading" class="empty-inline">正在读取 91卡券订单</div>
<table v-else class="data-table">
<thead>
<tr>
<th>订单</th>
<th>商品</th>
<th>状态</th>
<th>时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in ninetyoneOrders.items" :key="item.orderId">
<td>
<div class="cell-stack">
<strong>{{ item.orderNo }}</strong>
<span class="cell-subtle">{{ item.outTradeNo || '-' }}</span>
</div>
</td>
<td>
<div class="cell-stack">
<strong>{{ item.productName || item.productNo || '-' }}</strong>
<span class="cell-subtle">productNo: {{ item.productNo || '-' }}</span>
<span class="cell-subtle">数量{{ item.buyNum || 0 }}</span>
</div>
</td>
<td>
<div class="cell-stack">
<strong>{{ item.orderStatus }}</strong>
<span class="cell-subtle">任务 {{ item.taskCount }}</span>
<span v-if="item.failReason" class="cell-subtle">{{ item.failReason }}</span>
</div>
</td>
<td>{{ formatAdminDateTime(item.updatedAt || item.createdAt) }}</td>
<td>
<div class="row-actions">
<el-button
:loading="ninetyoneActionLoadingId === item.orderId"
link
type="primary"
@click="handleNinetyoneRetryOrder(item)"
>
重试生成任务
</el-button>
<el-button
v-if="item.orderStatus !== 'manual_failed'"
:loading="ninetyoneActionLoadingId === item.orderId"
link
type="danger"
@click="handleNinetyoneFailOrder(item)"
>
标记失败
</el-button>
</div>
</td>
</tr>
<tr v-if="ninetyoneOrders.items.length === 0">
<td colspan="5" class="empty-inline">当前没有 91卡券订单</td>
</tr>
</tbody>
</table>
</section>
</template>
@@ -1,4 +1,4 @@
export type PlatformTab = 'agiso' | 'khhao' | 'kuaishouEticket' | 'cloudtentacles'
export type PlatformTab = 'agiso' | 'khhao' | 'ninetyone' | 'kuaishouEticket' | 'cloudtentacles'
export type EditableDefaults = {
messageTemplate: string
@@ -0,0 +1,117 @@
import { computed, ref } from 'vue'
import { showConfirm, showError, showSuccess } from '@/lib/feedback'
import {
failAdminNinetyoneOrder,
fetchAdminNinetyoneOrders,
retryAdminNinetyoneOrder,
} from '@/services/admin'
import type { AdminNinetyoneOrderItem, AdminNinetyoneOrderListResult } from '@/types/admin'
export function useAdminNinetyonePlatform() {
const ninetyoneLoading = ref(false)
const ninetyoneActionLoadingId = ref<number | null>(null)
const ninetyoneResultError = ref('')
const ninetyoneStatus = ref<'pending_config' | 'all' | 'manual_failed'>('pending_config')
const ninetyoneOrders = ref<AdminNinetyoneOrderListResult>({
page: 1,
pageSize: 20,
total: 0,
items: [],
})
const ninetyoneStats = computed(() => {
const pendingCount = ninetyoneOrders.value.items.filter((item) => item.orderStatus === 'pending_config').length
const failedCount = ninetyoneOrders.value.items.filter((item) => item.orderStatus === 'manual_failed').length
const taskReadyCount = ninetyoneOrders.value.items.filter((item) => item.taskCount > 0).length
return {
total: ninetyoneOrders.value.total,
currentCount: ninetyoneOrders.value.items.length,
pendingCount,
failedCount,
taskReadyCount,
}
})
async function loadNinetyoneOrders(page = 1) {
ninetyoneLoading.value = true
ninetyoneResultError.value = ''
try {
const response = await fetchAdminNinetyoneOrders({
page,
pageSize: ninetyoneOrders.value.pageSize || 20,
status: ninetyoneStatus.value,
})
ninetyoneOrders.value = response.data
} catch (error) {
ninetyoneResultError.value = error instanceof Error ? error.message : '91卡券订单读取失败'
showError(ninetyoneResultError.value)
} finally {
ninetyoneLoading.value = false
}
}
async function handleNinetyoneStatusChange(status: 'pending_config' | 'all' | 'manual_failed') {
ninetyoneStatus.value = status
await loadNinetyoneOrders(1)
}
async function handleNinetyoneRetryOrder(item: AdminNinetyoneOrderItem) {
ninetyoneActionLoadingId.value = item.orderId
ninetyoneResultError.value = ''
try {
const response = await retryAdminNinetyoneOrder(item.orderId)
showSuccess(`91卡券订单已重试,生成任务 ${response.data.taskCount}`)
await loadNinetyoneOrders(ninetyoneOrders.value.page || 1)
} catch (error) {
ninetyoneResultError.value = error instanceof Error ? error.message : '91卡券订单重试失败'
showError(ninetyoneResultError.value)
} finally {
ninetyoneActionLoadingId.value = null
}
}
async function handleNinetyoneFailOrder(item: AdminNinetyoneOrderItem) {
try {
await showConfirm(`确认把 91卡券订单 ${item.orderNo} 标记为无法履约吗?查询接口会返回失败状态。`, '确认标记失败', {
type: 'warning',
confirmButtonText: '标记失败',
cancelButtonText: '取消',
})
} catch {
return
}
ninetyoneActionLoadingId.value = item.orderId
ninetyoneResultError.value = ''
try {
await failAdminNinetyoneOrder(item.orderId, {
reason: '商家后台手动标记无法履约',
})
showSuccess('91卡券订单已标记失败')
await loadNinetyoneOrders(ninetyoneOrders.value.page || 1)
} catch (error) {
ninetyoneResultError.value = error instanceof Error ? error.message : '91卡券订单标记失败失败'
showError(ninetyoneResultError.value)
} finally {
ninetyoneActionLoadingId.value = null
}
}
return {
ninetyoneLoading,
ninetyoneActionLoadingId,
ninetyoneResultError,
ninetyoneStatus,
ninetyoneOrders,
ninetyoneStats,
loadNinetyoneOrders,
handleNinetyoneStatusChange,
handleNinetyoneRetryOrder,
handleNinetyoneFailOrder,
}
}
@@ -16,6 +16,8 @@ import type {
AdminKhhaoOrderSyncResult,
AdminKhhaoSourceConfig,
AdminKhhaoSyncState,
AdminNinetyoneOrderActionResult,
AdminNinetyoneOrderListResult,
AdminKuaishouCloudFulfillmentConfig,
AdminKuaishouEticketConsumeResult,
AdminKuaishouEticketDetailResult,
@@ -94,6 +96,41 @@ export function syncAdminKhhaoOrders(payload: {
return apiPost<AdminKhhaoOrderSyncResult>('/api/v1/admin/platform-config/khhao/sync-orders', payload)
}
export function fetchAdminNinetyoneOrders(params: {
page?: number
pageSize?: number
status?: string
} = {}) {
const searchParams = new URLSearchParams()
if (params.page) {
searchParams.set('page', String(params.page))
}
if (params.pageSize) {
searchParams.set('pageSize', String(params.pageSize))
}
if (params.status) {
searchParams.set('status', params.status)
}
const queryString = searchParams.toString()
return apiGet<AdminNinetyoneOrderListResult>(
`/api/v1/admin/platform-config/ninetyone/orders${queryString ? `?${queryString}` : ''}`,
)
}
export function retryAdminNinetyoneOrder(orderId: number | string) {
return apiPost<AdminNinetyoneOrderActionResult>(
`/api/v1/admin/platform-config/ninetyone/orders/${orderId}/retry`,
{},
)
}
export function failAdminNinetyoneOrder(orderId: number | string, payload: { reason?: string }) {
return apiPost<AdminNinetyoneOrderActionResult>(
`/api/v1/admin/platform-config/ninetyone/orders/${orderId}/fail`,
payload,
)
}
export function fetchAdminKuaishouEticketSourceConfig() {
return apiGet<{
filePath: string
@@ -54,6 +54,13 @@
flex-wrap: wrap;
}
.admin-platform-shops .row-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.admin-platform-shops .overview-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
@@ -550,4 +557,4 @@
.admin-platform-shops .shop-summary-side {
justify-items: start;
}
}
}
+33
View File
@@ -271,6 +271,39 @@ export interface AdminKhhaoOrderSyncResult {
results: AdminKhhaoOrderSyncItem[]
}
export interface AdminNinetyoneOrderItem {
orderId: number
orderNo: string
outTradeNo: string
orderStatus: string
payStatus: string
shopId: string
shopName: string
productNo: string
productName: string
buyNum: number
taskCount: number
failReason: string
createdAt: string
updatedAt: string
items: Array<Record<string, unknown>>
}
export interface AdminNinetyoneOrderListResult {
page: number
pageSize: number
total: number
items: AdminNinetyoneOrderItem[]
}
export interface AdminNinetyoneOrderActionResult {
orderId: number
orderNo: string
orderStatus: string
orderItemCount: number
taskCount: number
}
export interface AdminCloudtentaclesSourceConfig {
enabled: boolean
baseUrl: string
@@ -6,6 +6,7 @@ import {
fetchAdminCloudtentaclesSkuList,
fetchAdminKhhaoSourceConfig,
fetchAdminKuaishouCloudFulfillmentConfig,
fetchAdminNinetyoneOrders,
queryAdminKhhaoOrders,
saveAdminKuaishouCloudFulfillmentConfig,
} from '@/services/admin'
@@ -14,6 +15,7 @@ import type {
AdminKhhaoOrderPreview,
AdminKuaishouCloudFulfillmentConfig,
AdminKuaishouCloudFulfillmentItem,
AdminNinetyoneOrderItem,
} from '@/types/admin'
import { hasAdminRole } from '@/utils/admin-auth'
@@ -41,6 +43,10 @@ const khhaoLookupLoading = ref(false)
const khhaoLookupErrorMessage = ref('')
const khhaoLookupResults = ref<AdminKhhaoOrderPreview[]>([])
const importedKhhaoOrderKeys = ref<string[]>([])
const ninetyoneLookupLoading = ref(false)
const ninetyoneLookupErrorMessage = ref('')
const ninetyoneLookupResults = ref<AdminNinetyoneOrderItem[]>([])
const importedNinetyoneOrderKeys = ref<string[]>([])
const cloudSkuCatalogLoading = ref(false)
const cloudSkuCatalogErrorMessage = ref('')
const cloudSkuCatalog = ref<AdminCloudtentaclesSkuItem[]>([])
@@ -52,6 +58,11 @@ const khhaoLookupForm = reactive({
limit: 10,
maxCaptchaAttempts: 3,
})
const ninetyoneLookupForm = reactive({
status: 'pending_config' as 'pending_config' | 'all' | 'manual_failed',
page: 1,
pageSize: 20,
})
const metrics = computed(() => {
const readyCount = items.value.filter(isItemComplete).length
@@ -90,6 +101,17 @@ const khhaoLookupMetrics = computed(() => {
}
})
const ninetyoneLookupMetrics = computed(() => {
const importedCount = ninetyoneLookupResults.value.filter((item) => isNinetyoneProductImported(item)).length
const pendingCount = ninetyoneLookupResults.value.filter((item) => item.orderStatus === 'pending_config').length
return {
total: ninetyoneLookupResults.value.length,
importedCount,
availableCount: Math.max(ninetyoneLookupResults.value.length - importedCount, 0),
pendingCount,
}
})
function createEmptyItem(): EditableItem {
return {
localId: crypto.randomUUID(),
@@ -423,6 +445,38 @@ function createItemFromKhhaoOrder(item: AdminKhhaoOrderPreview): EditableItem {
}
}
function createItemFromNinetyoneOrder(item: AdminNinetyoneOrderItem): EditableItem {
const productNo = String(item.productNo || '').trim()
const productName = String(item.productName || productNo).trim()
return {
localId: crypto.randomUUID(),
id: '',
enabled: true,
priority: 100,
provider: '91kaquan',
platform: 'kuaishou',
shopId: item.shopId || '91kaquan',
khhaoShopId: '',
internalSkuCode: '',
internalSkuName: productName,
externalSkuCode: productNo,
externalItemId: productNo,
externalSkuName: productName,
resolvedSkuName: productName,
cloudSourceKey: 'default',
cloudSkuId: 0,
cloudSkuName: '',
vnKey: '1',
autoBuyEnabled: true,
minAssetReserve: 0,
autoReturnNumberAfterDispatch: false,
autoConsumeAfterDispatch: false,
kuaishouConsumeShopId: '',
kuaishouConsumeShopName: '',
notes: `从 91卡券订单 ${item.orderNo} 导入`,
}
}
function findExistingItemFromKhhaoOrder(order: AdminKhhaoOrderPreview) {
const orderShopId = String(order.kuaishouShopId || order.shopId || '').trim()
const orderItemId = String(order.itemId || '').trim()
@@ -441,6 +495,23 @@ function findExistingItemFromKhhaoOrder(order: AdminKhhaoOrderPreview) {
}) || null
}
function findExistingItemFromNinetyoneOrder(order: AdminNinetyoneOrderItem) {
const productNo = String(order.productNo || '').trim()
const productName = String(order.productName || '').trim()
const shopId = String(order.shopId || '91kaquan').trim()
return items.value.find((item) => {
const sameSource = item.provider.trim() === '91kaquan' && item.platform.trim() === 'kuaishou'
const sameShop = !shopId || item.shopId.trim() === shopId
const sameProductNo = productNo && (
item.externalSkuCode.trim() === productNo
|| item.externalItemId.trim() === productNo
)
const sameName = productName && [item.externalSkuName, item.internalSkuName, item.resolvedSkuName].some((value) => value.trim() === productName)
return sameSource && sameShop && (sameProductNo || sameName)
}) || null
}
async function importKhhaoProduct(item: AdminKhhaoOrderPreview) {
validationState.value = null
const importKey = getKhhaoOrderImportKey(item)
@@ -466,6 +537,31 @@ async function importKhhaoProduct(item: AdminKhhaoOrderPreview) {
await focusValidationTarget(next.localId)
}
async function importNinetyoneProduct(item: AdminNinetyoneOrderItem) {
validationState.value = null
const importKey = getNinetyoneOrderImportKey(item)
const existing = findExistingItemFromNinetyoneOrder(item)
if (existing) {
setCollapsed(existing.localId, false)
if (!importedNinetyoneOrderKeys.value.includes(importKey)) {
importedNinetyoneOrderKeys.value = [...importedNinetyoneOrderKeys.value, importKey]
}
showSuccess('已定位到现有 91卡券规则草稿,直接继续完善即可')
await focusValidationTarget(existing.localId)
return
}
const next = createItemFromNinetyoneOrder(item)
items.value.unshift(next)
setCollapsed(next.localId, false)
if (!importedNinetyoneOrderKeys.value.includes(importKey)) {
importedNinetyoneOrderKeys.value = [...importedNinetyoneOrderKeys.value, importKey]
}
await focusValidationTarget(next.localId)
}
function getKhhaoOrderImportKey(item: AdminKhhaoOrderPreview) {
return [
item.platformOrderId,
@@ -481,6 +577,20 @@ function isKhhaoProductImported(item: AdminKhhaoOrderPreview) {
return importedKhhaoOrderKeys.value.includes(getKhhaoOrderImportKey(item))
}
function getNinetyoneOrderImportKey(item: AdminNinetyoneOrderItem) {
return [
item.orderNo,
item.productNo,
item.shopId || '91kaquan',
]
.map((value) => String(value || '').trim())
.join(':')
}
function isNinetyoneProductImported(item: AdminNinetyoneOrderItem) {
return importedNinetyoneOrderKeys.value.includes(getNinetyoneOrderImportKey(item))
}
async function ensureCloudSkuCatalogLoaded(force = false) {
if (!force && cloudSkuCatalog.value.length > 0) {
return cloudSkuCatalog.value
@@ -686,6 +796,26 @@ async function lookupKhhaoProducts() {
}
}
async function lookupNinetyoneProducts() {
ninetyoneLookupLoading.value = true
ninetyoneLookupErrorMessage.value = ''
try {
const response = await fetchAdminNinetyoneOrders({
page: Number(ninetyoneLookupForm.page || 1),
pageSize: Number(ninetyoneLookupForm.pageSize || 20),
status: ninetyoneLookupForm.status,
})
ninetyoneLookupResults.value = response.data.items
importedNinetyoneOrderKeys.value = []
} catch (error) {
ninetyoneLookupResults.value = []
ninetyoneLookupErrorMessage.value = error instanceof Error ? error.message : '91卡券订单查询失败'
} finally {
ninetyoneLookupLoading.value = false
}
}
onMounted(loadConfigs)
</script>
@@ -745,6 +875,7 @@ onMounted(loadConfigs)
<div class="overview-notes">
<span class="overview-note">khhao 订单只用于取样导入不写入订单库</span>
<span class="overview-note">91卡券订单来自已接收的待补全队列</span>
<span class="overview-note">内部 SKU 决定最终任务与库存绑定</span>
<span class="overview-note">cloud SKU 决定自动购买发货与退号资源</span>
</div>
@@ -860,6 +991,104 @@ onMounted(loadConfigs)
</table>
</section>
<section class="table-card table-card--dense">
<div class="section-title-row section-title-row--tight">
<div>
<h3>91卡券订单取样导入</h3>
<p>查询已接收的 91卡券订单 productNo 导入为规则草稿再补齐内部 SKU cloud 资源</p>
</div>
<div class="section-mini-stats">
<span class="mini-stat-chip">结果 {{ ninetyoneLookupMetrics.total }}</span>
<span class="mini-stat-chip">待补 {{ ninetyoneLookupMetrics.pendingCount }}</span>
<span class="mini-stat-chip">待导入 {{ ninetyoneLookupMetrics.availableCount }}</span>
</div>
</div>
<div class="khhao-toolbar">
<label class="field-block">
<span>订单状态</span>
<select v-model="ninetyoneLookupForm.status" class="text-input">
<option value="pending_config">待补全</option>
<option value="manual_failed">已失败</option>
<option value="all">全部</option>
</select>
</label>
<label class="field-block">
<span>页码</span>
<input v-model.number="ninetyoneLookupForm.page" class="text-input" min="1" type="number" />
</label>
<label class="field-block">
<span>每页条数</span>
<input v-model.number="ninetyoneLookupForm.pageSize" class="text-input" max="100" min="1" type="number" />
</label>
<div class="field-block field-block--action">
<span>操作</span>
<el-button :loading="ninetyoneLookupLoading" round type="primary" @click="lookupNinetyoneProducts">查询 91卡券订单</el-button>
</div>
</div>
<p class="toolbar-hint">导入后会创建 provider=91kaquanplatform=kuaishou外部 SKU=productNo 的规则草稿保存规则后回到平台配置 -> 91卡券接入重试订单</p>
<p v-if="ninetyoneLookupErrorMessage" class="error-copy lookup-error">{{ ninetyoneLookupErrorMessage }}</p>
<div v-else-if="ninetyoneLookupLoading" class="empty-inline">正在查询 91卡券订单</div>
<table v-else class="data-table">
<thead>
<tr>
<th>订单</th>
<th>来源</th>
<th>商品</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in ninetyoneLookupResults" :key="getNinetyoneOrderImportKey(item)">
<td>
<div class="cell-stack">
<strong>{{ item.orderNo || '-' }}</strong>
<span class="cell-subtle">{{ item.outTradeNo || '-' }}</span>
</div>
</td>
<td>
<div class="cell-stack">
<strong>{{ item.shopName || '91卡券' }}</strong>
<span class="cell-subtle">provider: 91kaquan</span>
<span class="cell-subtle">shopId: {{ item.shopId || '91kaquan' }}</span>
</div>
</td>
<td>
<div class="cell-stack">
<strong>{{ item.productName || item.productNo || '-' }}</strong>
<span class="cell-subtle">productNo: {{ item.productNo || '-' }}</span>
<span class="cell-subtle">数量{{ item.buyNum || 0 }}</span>
</div>
</td>
<td>
<div class="cell-stack">
<strong>{{ item.orderStatus || '-' }}</strong>
<span class="cell-subtle">任务 {{ item.taskCount || 0 }}</span>
</div>
</td>
<td>
<el-button
v-if="!isNinetyoneProductImported(item)"
link
type="primary"
@click="importNinetyoneProduct(item)"
>
导入到规则
</el-button>
<span v-else class="cell-subtle">已导入草稿</span>
</td>
</tr>
<tr v-if="ninetyoneLookupResults.length === 0">
<td colspan="5" class="empty-inline">还没有 91卡券查询结果</td>
</tr>
</tbody>
</table>
</section>
<section class="table-card table-card--dense">
<div class="section-title-row section-title-row--tight">
<div>
@@ -924,13 +1153,13 @@ onMounted(loadConfigs)
<section class="mapping-panel mapping-panel--external">
<div class="mapping-panel-head">
<strong>外部商品</strong>
<span>khhao / 快手侧命中条件</span>
<span>来源平台 / 快手侧命中条件</span>
</div>
<div class="mapping-grid">
<label class="field-block">
<span>来源 provider</span>
<input v-model="item.provider" class="text-input" maxlength="32" placeholder="默认 khhao" />
<input v-model="item.provider" class="text-input" maxlength="32" placeholder="khhao / 91kaquan" />
</label>
<label class="field-block">
<span>来源 platform</span>
@@ -942,7 +1171,7 @@ onMounted(loadConfigs)
</label>
<label class="field-block">
<span>khhao 店铺 ID</span>
<input v-model="item.khhaoShopId" class="text-input" maxlength="80" placeholder="例如 10,可用于 khhao 内部店铺匹配" />
<input v-model="item.khhaoShopId" class="text-input" maxlength="80" placeholder="仅 khhao 使用,例如 10" />
</label>
<label class="field-block">
<span>外部 SKU</span>
@@ -4,10 +4,12 @@ import { onMounted, ref } from 'vue'
import AdminPlatformAgisoSection from '@/components/admin/AdminPlatformAgisoSection.vue'
import AdminPlatformCloudtentaclesSection from '@/components/admin/AdminPlatformCloudtentaclesSection.vue'
import AdminPlatformKhhaoSection from '@/components/admin/AdminPlatformKhhaoSection.vue'
import AdminPlatformNinetyoneSection from '@/components/admin/AdminPlatformNinetyoneSection.vue'
import AdminPlatformKuaishouEticketSection from '@/components/admin/AdminPlatformKuaishouEticketSection.vue'
import { useAdminAgisoPlatform } from '@/composables/admin/platform-shops/useAdminAgisoPlatform'
import { useAdminCloudtentaclesPlatform } from '@/composables/admin/platform-shops/useAdminCloudtentaclesPlatform'
import { useAdminKhhaoPlatform } from '@/composables/admin/platform-shops/useAdminKhhaoPlatform'
import { useAdminNinetyonePlatform } from '@/composables/admin/platform-shops/useAdminNinetyonePlatform'
import { useAdminKuaishouEticketPlatform } from '@/composables/admin/platform-shops/useAdminKuaishouEticketPlatform'
import type { PlatformTab } from '@/composables/admin/platform-shops/types'
import {
@@ -64,6 +66,19 @@ const {
handleKhhaoSyncOrders,
} = useAdminKhhaoPlatform()
const {
ninetyoneLoading,
ninetyoneActionLoadingId,
ninetyoneResultError,
ninetyoneStatus,
ninetyoneOrders,
ninetyoneStats,
loadNinetyoneOrders,
handleNinetyoneStatusChange,
handleNinetyoneRetryOrder,
handleNinetyoneFailOrder,
} = useAdminNinetyonePlatform()
const {
kuaishouEticketFilePath,
kuaishouEticketShops,
@@ -152,6 +167,7 @@ async function loadConfigs() {
hydrateKhhaoConfig(khhaoResponse.data)
hydrateKuaishouEticketConfig(kuaishouEticketResponse.data)
hydrateCloudtentaclesConfig(cloudtentaclesResponse.data)
await loadNinetyoneOrders(1)
} catch (error) {
errorMessage.value = error instanceof Error ? error.message : '读取平台配置失败'
} finally {
@@ -208,6 +224,23 @@ onMounted(loadConfigs)
</div>
</button>
<button
type="button"
:class="['platform-overview-card', { 'is-active': activePlatform === 'ninetyone' }]"
@click="switchPlatform('ninetyone')"
>
<div class="platform-head">
<span class="platform-badge">91卡券</span>
<span class="platform-tag">接入 / 待补全</span>
</div>
<strong>{{ ninetyoneStats.pendingCount }}</strong>
<span>待补全订单</span>
<div class="platform-metrics">
<span>当前 {{ ninetyoneStats.currentCount }} </span>
<span>已生成任务 {{ ninetyoneStats.taskReadyCount }} </span>
</div>
</button>
<button
type="button"
:class="['platform-overview-card', { 'is-active': activePlatform === 'khhao' }]"
@@ -276,6 +309,13 @@ onMounted(loadConfigs)
>
khhao 来源与同步
</button>
<button
type="button"
:class="['switch-chip', { 'is-active': activePlatform === 'ninetyone' }]"
@click="switchPlatform('ninetyone')"
>
91卡券接入
</button>
<button
type="button"
:class="['switch-chip', { 'is-active': activePlatform === 'kuaishouEticket' }]"
@@ -330,6 +370,20 @@ onMounted(loadConfigs)
:handle-khhao-sync-orders="handleKhhaoSyncOrders"
/>
<AdminPlatformNinetyoneSection
v-else-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"
/>
<AdminPlatformKuaishouEticketSection
v-else-if="activePlatform === 'kuaishouEticket'"
:kuaishou-eticket-file-path="kuaishouEticketFilePath"