补全一些基础信息
This commit is contained in:
@@ -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=91kaquan、platform=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"
|
||||
|
||||
Reference in New Issue
Block a user