优化了部分ui

This commit is contained in:
yml
2026-05-04 00:00:15 +08:00
parent f07a4f8dcb
commit dd842c165a
7 changed files with 228 additions and 80 deletions
+1
View File
@@ -95,6 +95,7 @@ module.exports = {
vnVerifCodePath: '/public/vn_verif_code',
vnVerifyLoginCodePath: '/vn/verify_login_code',
vnBindUrlPath: '/vn/bind_url',
vnBindInfoPath: '/vn/bind_info',
vnBackPath: '/vn/back',
clientSource: 'ct-client',
deviceId: '-',
+5 -5
View File
@@ -1,13 +1,13 @@
{
"syncFromCreatedAt": "2026-05-02T10:53:28.550Z",
"lastRunStartedAt": "2026-05-03T15:01:47.533Z",
"lastRunFinishedAt": "2026-05-03T15:01:48.438Z",
"lastRunStartedAt": "2026-05-03T15:58:17.921Z",
"lastRunFinishedAt": "2026-05-03T15:58:18.685Z",
"lastRunStatus": "success",
"lastErrorMessage": "",
"fetchedCount": 40,
"syncedCount": 0,
"ignoredCount": 40,
"lastOrderCreatedAt": "2026-05-03T15:01:23.000Z",
"syncedCount": 1,
"ignoredCount": 39,
"lastOrderCreatedAt": "2026-05-03T15:57:42.000Z",
"watchMode": "normal",
"watchOrders": []
}
@@ -1,4 +1,30 @@
{
"enabled": true,
"items": []
"items": [
{
"id": "套装-浪漫天命",
"enabled": true,
"priority": 100,
"provider": "khhao",
"platform": "kuaishou",
"shopId": "4269276762",
"internalSkuCode": "套装-浪漫天命",
"internalSkuName": "测试1",
"externalSkuCode": "830",
"externalItemId": "830",
"externalSkuName": "测试1",
"resolvedSkuName": "测试1",
"cloudSourceKey": "default",
"cloudSkuId": 28,
"cloudSkuName": "套装-浪漫天命",
"vnKey": "1",
"autoBuyEnabled": true,
"minAssetReserve": 0,
"autoReturnNumberAfterDispatch": true,
"autoConsumeAfterDispatch": true,
"kuaishouConsumeShopId": "4269276762",
"kuaishouConsumeShopName": "稚嫩游戏交易店",
"notes": "从 khhao 订单 2612300173165561 导入"
}
]
}
@@ -156,12 +156,22 @@ function normalizeKuaishouCloudFlow(value) {
},
binding: {
prepareStatus: String(binding.prepareStatus || 'pending').trim() || 'pending',
cloudSourceKey: String(binding.cloudSourceKey || '').trim(),
skuId: Number(binding.skuId || 0) || 0,
skuName: String(binding.skuName || '').trim(),
vnKey: String(binding.vnKey || '').trim(),
vnId: Number(binding.vnId || 0) || 0,
vnPhone: String(binding.vnPhone || '').trim(),
bindUrl: String(binding.bindUrl || '').trim(),
bindPreparedAt: binding.bindPreparedAt || null,
},
consume: {
status: String(consume.status || 'pending').trim() || 'pending',
shopId: String(consume.shopId || '').trim(),
shopName: String(consume.shopName || '').trim(),
autoConsumeEnabled: consume.autoConsumeEnabled === true,
consumedAt: consume.consumedAt || null,
errorMessage: String(consume.errorMessage || '').trim(),
},
}
}
+1
View File
@@ -115,6 +115,7 @@ export {}
* vnVerifCodePath: string
* vnVerifyLoginCodePath: string
* vnBindUrlPath: string
* vnBindInfoPath: string
* vnBackPath: string
* publicKeyPem: string
* clientSource: string
+3
View File
@@ -22,6 +22,9 @@ declare module 'vue' {
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
RouterLink: typeof import('vue-router')['RouterLink']
@@ -912,80 +912,104 @@ onMounted(loadConfigs)
<span>核销店铺{{ getConsumeShopSummary(item) }}</span>
</div>
<div v-else class="binding-grid">
<label class="field-block">
<span>内部 SKU</span>
<input v-model="item.internalSkuCode" class="text-input" maxlength="80" placeholder="必填,用于生成任务" />
</label>
<label class="field-block field-wide">
<span>内部商品名</span>
<input v-model="item.internalSkuName" class="text-input" maxlength="120" placeholder="展示用,可选但建议填" />
</label>
<label class="field-block">
<span>优先级</span>
<input v-model.number="item.priority" class="text-input" min="1" type="number" />
</label>
<label class="field-block">
<span>来源 provider</span>
<input v-model="item.provider" class="text-input" maxlength="32" placeholder="默认 khhao" />
</label>
<label class="field-block">
<span>来源 platform</span>
<input v-model="item.platform" class="text-input" maxlength="32" placeholder="默认 kuaishou" />
</label>
<label class="field-block">
<span>店铺 ID</span>
<input v-model="item.shopId" class="text-input" maxlength="80" placeholder="留空表示跨店铺共用" />
</label>
<label class="field-block field-wide">
<span>解析商品名</span>
<input v-model="item.resolvedSkuName" class="text-input" maxlength="120" placeholder="可作为商品名兜底" />
</label>
<div v-else class="mapping-editor">
<section class="mapping-panel mapping-panel--external">
<div class="mapping-panel-head">
<strong>外部商品</strong>
<span>khhao / 快手侧命中条件</span>
</div>
<label class="field-block">
<span>外部 SKU</span>
<input v-model="item.externalSkuCode" class="text-input" maxlength="120" placeholder="例如 khhao 的 skuCode" />
</label>
<label class="field-block">
<span>外部商品 ID</span>
<input v-model="item.externalItemId" class="text-input" maxlength="120" placeholder="平台商品 ID" />
</label>
<label class="field-block field-wide">
<span>外部商品名</span>
<input v-model="item.externalSkuName" class="text-input" maxlength="200" placeholder="用于名称匹配" />
</label>
<div class="mapping-grid">
<label class="field-block">
<span>来源 provider</span>
<input v-model="item.provider" class="text-input" maxlength="32" placeholder="默认 khhao" />
</label>
<label class="field-block">
<span>来源 platform</span>
<input v-model="item.platform" class="text-input" maxlength="32" placeholder="默认 kuaishou" />
</label>
<label class="field-block field-wide">
<span>店铺 ID</span>
<input v-model="item.shopId" class="text-input" maxlength="80" placeholder="留空表示跨店铺共用" />
</label>
<label class="field-block">
<span>外部 SKU</span>
<input v-model="item.externalSkuCode" class="text-input" maxlength="120" placeholder="例如 khhao 的 skuCode" />
</label>
<label class="field-block">
<span>外部商品 ID</span>
<input v-model="item.externalItemId" class="text-input" maxlength="120" placeholder="平台商品 ID" />
</label>
<label class="field-block field-wide">
<span>外部商品名</span>
<input v-model="item.externalSkuName" class="text-input" maxlength="200" placeholder="用于名称匹配" />
</label>
<label class="field-block field-wide">
<span>解析商品名</span>
<input v-model="item.resolvedSkuName" class="text-input" maxlength="120" placeholder="可作为商品名兜底" />
</label>
</div>
</section>
<label class="field-block">
<span>cloudSourceKey</span>
<input v-model="item.cloudSourceKey" class="text-input" maxlength="60" placeholder="默认 default" />
</label>
<label class="field-block">
<span>cloud SKU ID</span>
<select
v-model.number="item.cloudSkuId"
class="text-input"
@change="handleCloudSkuSelected(item, item.cloudSkuId)"
@focus="handleCloudSkuDropdownVisible(item, true)"
>
<option :value="0">请选择 cloud SKU</option>
<option v-for="sku in getCloudSkuOptions(item)" :key="sku.id" :value="sku.id">
{{ formatCloudSkuOptionLabel(sku) }}
</option>
</select>
<small class="field-help">先填内部 SKU / 商品名再选择会更容易匹配</small>
</label>
<label class="field-block field-wide">
<span>cloud SKU 名称</span>
<input :value="item.cloudSkuName" class="text-input text-input--readonly" placeholder="选择 cloud SKU 后自动带出" readonly />
</label>
<label class="field-block">
<span>虚拟号 VN Key</span>
<input class="text-input text-input--readonly" value="1" readonly />
</label>
<label class="field-block">
<span>最低保留余额</span>
<input v-model.number="item.minAssetReserve" class="text-input" min="0" type="number" />
</label>
<div class="mapping-arrow" aria-hidden="true">
<span>映射到</span>
</div>
<section class="mapping-panel mapping-panel--internal">
<div class="mapping-panel-head">
<strong>内部履约</strong>
<span>内部 SKU + cloud 资源</span>
</div>
<div class="mapping-grid">
<label class="field-block">
<span>内部 SKU</span>
<input v-model="item.internalSkuCode" class="text-input" maxlength="80" placeholder="必填,用于生成任务" />
</label>
<label class="field-block">
<span>优先级</span>
<input v-model.number="item.priority" class="text-input" min="1" type="number" />
</label>
<label class="field-block field-wide">
<span>内部商品名</span>
<input v-model="item.internalSkuName" class="text-input" maxlength="120" placeholder="展示用,可选但建议填" />
</label>
<label class="field-block">
<span>cloudSourceKey</span>
<input v-model="item.cloudSourceKey" class="text-input" maxlength="60" placeholder="默认 default" />
</label>
<label class="field-block">
<span>cloud SKU ID</span>
<select
v-model.number="item.cloudSkuId"
class="text-input"
@change="handleCloudSkuSelected(item, item.cloudSkuId)"
@focus="handleCloudSkuDropdownVisible(item, true)"
>
<option :value="0">请选择 cloud SKU</option>
<option v-for="sku in getCloudSkuOptions(item)" :key="sku.id" :value="sku.id">
{{ formatCloudSkuOptionLabel(sku) }}
</option>
</select>
<small class="field-help">先填内部 SKU / 商品名再选择会更容易匹配</small>
</label>
<label class="field-block field-wide">
<span>cloud SKU 名称</span>
<input :value="item.cloudSkuName" class="text-input text-input--readonly" placeholder="选择 cloud SKU 后自动带出" readonly />
</label>
<label class="field-block">
<span>虚拟号 VN Key</span>
<input class="text-input text-input--readonly" value="1" readonly />
</label>
<label class="field-block">
<span>最低保留余额</span>
<input v-model.number="item.minAssetReserve" class="text-input" min="0" type="number" />
</label>
</div>
</section>
</div>
<div v-if="!isCollapsed(item.localId)" class="mapping-extra">
<label class="field-block">
<span>快手核销店铺 ID</span>
<input v-model="item.kuaishouConsumeShopId" class="text-input" maxlength="80" placeholder="预留给后续自动核销" />
@@ -2040,6 +2064,81 @@ onMounted(loadConfigs)
gap: 12px;
}
.mapping-editor {
display: grid;
grid-template-columns: minmax(0, 1fr) 74px minmax(0, 1fr);
gap: 12px;
align-items: stretch;
}
.mapping-panel {
display: grid;
align-content: start;
gap: 12px;
padding: 14px;
border-radius: 16px;
border: 1px solid rgba(148, 163, 184, 0.18);
}
.mapping-panel--external {
background: rgba(239, 246, 255, 0.72);
}
.mapping-panel--internal {
background: rgba(255, 255, 255, 0.86);
border-color: rgba(15, 93, 216, 0.16);
}
.mapping-panel-head {
display: flex;
justify-content: space-between;
gap: 10px;
align-items: baseline;
}
.mapping-panel-head strong {
color: #1d3555;
font-size: 15px;
}
.mapping-panel-head span {
color: #64748b;
font-size: 12px;
}
.mapping-grid,
.mapping-extra {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px 12px;
}
.mapping-arrow {
display: grid;
place-items: center;
color: #175cd3;
font-size: 12px;
font-weight: 700;
}
.mapping-arrow span {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 30px;
min-width: 54px;
padding: 0 10px;
border-radius: 999px;
background: rgba(15, 93, 216, 0.08);
border: 1px solid rgba(15, 93, 216, 0.14);
}
.mapping-extra {
margin-top: 12px;
padding-top: 12px;
border-top: 1px dashed rgba(86, 108, 138, 0.18);
}
.checkbox-line {
display: inline-flex;
align-items: center;
@@ -2068,10 +2167,15 @@ onMounted(loadConfigs)
@media (max-width: 1100px) {
.overview-stats,
.khhao-toolbar,
.binding-grid {
.binding-grid,
.mapping-editor {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.mapping-arrow {
display: none;
}
.field-wide {
grid-column: span 2;
}
@@ -2089,7 +2193,10 @@ onMounted(loadConfigs)
.overview-stats,
.khhao-toolbar,
.binding-grid {
.binding-grid,
.mapping-editor,
.mapping-grid,
.mapping-extra {
grid-template-columns: 1fr;
}