优化履约配置界面

This commit is contained in:
yml2213
2026-05-26 14:16:24 +08:00
parent 92cd88c1b2
commit ab417ca90f
2 changed files with 342 additions and 153 deletions
@@ -285,6 +285,150 @@
border: 1px solid var(--border-default);
}
.rule-editor {
display: grid;
gap: var(--space-3);
}
.rule-editor-main {
display: grid;
grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
gap: var(--space-3);
align-items: stretch;
}
.rule-panel {
display: grid;
align-content: start;
gap: var(--space-4);
min-width: 0;
padding: var(--space-4);
border: 1px solid var(--border-default);
border-radius: var(--radius-md);
background: var(--bg-surface);
}
.rule-panel--strategy {
background: var(--bg-subtle);
}
.rule-panel-head {
display: flex;
justify-content: space-between;
gap: var(--space-3);
align-items: center;
min-width: 0;
}
.rule-panel-head > span {
color: var(--text-secondary);
font-size: var(--text-xs);
line-height: 1.5;
text-align: right;
}
.rule-panel-title {
display: flex;
align-items: center;
gap: var(--space-2);
min-width: 0;
color: var(--text-primary);
}
.rule-panel-title strong {
font-size: var(--text-base);
font-weight: 700;
}
.rule-step {
display: inline-grid;
place-items: center;
width: 30px;
height: 24px;
border-radius: var(--radius-sm);
background: var(--bg-subtle);
color: var(--text-secondary);
font-size: var(--text-xs);
font-weight: 700;
}
.rule-panel--strategy .rule-step {
background: var(--bg-surface);
}
.mapping-grid--core {
gap: var(--space-3) var(--space-4);
}
.strategy-grid {
display: grid;
grid-template-columns: minmax(150px, 0.24fr) minmax(150px, 0.24fr) minmax(0, 1fr);
gap: var(--space-4);
align-items: end;
}
.strategy-checks {
display: flex;
flex-wrap: wrap;
gap: var(--space-2) var(--space-4);
min-height: 32px;
align-items: center;
}
.advanced-fields {
border: 1px dashed var(--border-default);
border-radius: var(--radius-md);
background: var(--bg-surface);
}
.advanced-fields summary {
display: flex;
justify-content: space-between;
gap: var(--space-3);
align-items: center;
padding: var(--space-3) var(--space-4);
color: var(--text-primary);
cursor: pointer;
list-style: none;
}
.advanced-fields summary::-webkit-details-marker {
display: none;
}
.advanced-fields summary span {
font-weight: 700;
}
.advanced-fields summary small {
color: var(--text-secondary);
font-size: var(--text-xs);
line-height: 1.5;
text-align: right;
}
.advanced-fields summary::after {
content: '展开';
flex: 0 0 auto;
min-width: 40px;
color: var(--color-primary);
font-size: var(--text-xs);
font-weight: 700;
text-align: right;
}
.advanced-fields[open] summary {
border-bottom: 1px dashed var(--border-default);
}
.advanced-fields[open] summary::after {
content: '收起';
}
.advanced-grid {
padding: var(--space-4);
}
.field-wide {
grid-column: 1 / -1;
}
@@ -320,6 +464,7 @@
.binding-actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
gap: var(--space-3);
align-items: center;
padding-top: var(--space-3);
@@ -353,12 +498,26 @@
.overview-stats,
.lookup-toolbar,
.rule-editor-main,
.strategy-grid,
.mapping-editor,
.mapping-grid,
.mapping-extra {
grid-template-columns: 1fr;
}
.rule-panel-head,
.advanced-fields summary {
display: grid;
grid-template-columns: 1fr;
align-items: start;
}
.rule-panel-head > span,
.advanced-fields summary small {
text-align: left;
}
.mapping-arrow {
display: none;
}
@@ -77,16 +77,164 @@ const emit = defineEmits<{
}}</el-descriptions-item>
</el-descriptions>
<div v-else class="mapping-editor">
<el-card shadow="never" class="mapping-panel mapping-panel--external">
<template #header>
<div class="mapping-panel-head">
<strong>外部商品</strong>
<span>来源平台 / 快手侧命中条件</span>
<div v-else class="rule-editor">
<div class="rule-editor-main">
<section class="rule-panel rule-panel--match">
<div class="rule-panel-head">
<div class="rule-panel-title">
<span class="rule-step">01</span>
<strong>商品匹配</strong>
</div>
<span>订单进来后先命中这里</span>
</div>
</template>
<el-form label-position="top" class="mapping-grid">
<el-form label-position="top" class="mapping-grid mapping-grid--core">
<el-form-item label="外部 SKU / productNo" class="field-wide">
<el-input
v-model="item.externalSkuCode"
class="text-input"
maxlength="120"
placeholder="优先填写 91 卡券 productNo,例如 183999074512936"
/>
<small class="field-help">最推荐的命中条件没有特殊情况时只填这一项即可</small>
</el-form-item>
<el-form-item label="内部 SKU">
<el-input
v-model="item.internalSkuCode"
class="text-input"
maxlength="80"
placeholder="必填,例如 romantic-destiny-pack"
/>
</el-form-item>
<el-form-item label="内部商品名">
<el-input
v-model="item.internalSkuName"
class="text-input"
maxlength="120"
placeholder="后台识别用,建议填清楚"
/>
</el-form-item>
</el-form>
</section>
<section class="rule-panel rule-panel--delivery">
<div class="rule-panel-head">
<div class="rule-panel-title">
<span class="rule-step">02</span>
<strong>履约资源</strong>
</div>
<span>账号cloud SKU 和核销店铺</span>
</div>
<el-form label-position="top" class="mapping-grid mapping-grid--core">
<el-form-item label="cloud 账号优先级" class="field-wide">
<el-select
v-model="item.cloudSourceKeys"
class="text-input"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="按顺序选择可用于履约的 cloud 账号"
@change="emit('handleCloudSourceKeysChanged')"
>
<el-option
v-for="opt in cloudtentaclesSourceOptions"
:key="opt.key"
:label="opt.label || opt.key"
:value="opt.key"
/>
</el-select>
<small class="field-help">执行时从左到右尝试SKU 列表使用第一个账号加载</small>
</el-form-item>
<el-form-item label="cloud SKU">
<el-select
v-model="item.cloudSkuId"
class="text-input"
placeholder="请选择 cloud SKU"
@change="emit('handleCloudSkuSelected', item.cloudSkuId)"
@visible-change="emit('handleCloudSkuDropdownVisible', $event)"
>
<el-option label="请选择 cloud SKU" :value="0" />
<el-option
v-for="sku in getCloudSkuOptions(item)"
:key="sku.id"
:label="formatCloudSkuOptionLabel(sku)"
:value="sku.id"
/>
</el-select>
</el-form-item>
<el-form-item label="cloud SKU 名称">
<el-input
:model-value="item.cloudSkuName"
class="text-input"
placeholder="选择后自动带出"
readonly
/>
</el-form-item>
<el-form-item label="快手核销店铺" class="field-wide">
<el-select
v-model="item.kuaishouConsumeShopId"
class="text-input"
placeholder="请选择已配置 Cookie 的快手小店"
@change="emit('handleKuaishouConsumeShopSelected')"
>
<el-option label="请选择已配置 Cookie 的快手小店" value="" />
<el-option
v-for="shop in getKuaishouConsumeShopOptions()"
:key="shop.shopId"
:label="formatKuaishouConsumeShopOption(shop)"
:value="shop.shopId"
/>
</el-select>
<small class="field-help">来自平台店铺 -> 快手小店核销配置</small>
</el-form-item>
</el-form>
</section>
</div>
<section class="rule-panel rule-panel--strategy">
<div class="rule-panel-head">
<div class="rule-panel-title">
<span class="rule-step">03</span>
<strong>执行策略</strong>
</div>
<span>一般保持默认即可</span>
</div>
<el-form label-position="top" class="strategy-grid">
<el-form-item label="优先级">
<el-input-number
v-model="item.priority"
class="text-input"
:min="1"
controls-position="right"
/>
</el-form-item>
<el-form-item label="最低保留余额">
<el-input-number
v-model="item.minAssetReserve"
class="text-input"
:min="0"
controls-position="right"
/>
</el-form-item>
<el-form-item label="自动动作" class="field-wide">
<div class="strategy-checks">
<el-checkbox v-model="item.enabled">启用规则</el-checkbox>
<el-checkbox v-model="item.autoBuyEnabled">自动购买</el-checkbox>
<el-checkbox v-model="item.autoReturnNumberAfterDispatch">发货后退号</el-checkbox>
<el-checkbox v-model="item.autoConsumeAfterDispatch">发货后核销</el-checkbox>
</div>
</el-form-item>
</el-form>
</section>
<details class="advanced-fields">
<summary>
<span>高级匹配与备注</span>
<small>多店铺多平台或人工排查时再展开</small>
</summary>
<el-form label-position="top" class="mapping-grid advanced-grid">
<el-form-item label="来源 provider">
<el-input
v-model="item.provider"
@@ -103,7 +251,7 @@ const emit = defineEmits<{
placeholder="默认 kuaishou"
/>
</el-form-item>
<el-form-item label="店铺 ID" class="field-wide">
<el-form-item label="店铺 ID">
<el-input
v-model="item.shopId"
class="text-input"
@@ -111,20 +259,12 @@ const emit = defineEmits<{
placeholder="留空表示跨店铺共用"
/>
</el-form-item>
<el-form-item label="外部 SKU">
<el-input
v-model="item.externalSkuCode"
class="text-input"
maxlength="120"
placeholder="例如 91卡券 productNo"
/>
</el-form-item>
<el-form-item label="外部商品 ID">
<el-input
v-model="item.externalItemId"
class="text-input"
maxlength="120"
placeholder="平台商品 ID"
placeholder="平台商品 ID,可选"
/>
</el-form-item>
<el-form-item label="外部商品名" class="field-wide">
@@ -132,153 +272,47 @@ const emit = defineEmits<{
v-model="item.externalSkuName"
class="text-input"
maxlength="200"
placeholder="用于名称匹配"
placeholder="只有需要按名称匹配时再填"
/>
</el-form-item>
<el-form-item label="解析商品名" class="field-wide">
<el-form-item label="解析商品名">
<el-input
v-model="item.resolvedSkuName"
class="text-input"
maxlength="120"
placeholder="可作为商品名兜底"
/>
</el-form-item>
</el-form>
</el-card>
<div class="mapping-arrow" aria-hidden="true">
<span>映射到</span>
</div>
<el-card shadow="never" class="mapping-panel mapping-panel--internal">
<template #header>
<div class="mapping-panel-head">
<strong>内部履约</strong>
<span>内部 SKU + cloud 资源</span>
</div>
</template>
<el-form label-position="top" class="mapping-grid">
<el-form-item label="内部 SKU">
<el-input
v-model="item.internalSkuCode"
class="text-input"
maxlength="80"
placeholder="必填,用于生成任务"
/>
</el-form-item>
<el-form-item label="优先级">
<el-input-number
v-model="item.priority"
class="text-input"
:min="1"
controls-position="right"
/>
</el-form-item>
<el-form-item label="内部商品名" class="field-wide">
<el-input
v-model="item.internalSkuName"
class="text-input"
maxlength="120"
placeholder="展示用,可选但建议填"
/>
</el-form-item>
<el-form-item label="cloud 账号优先级" class="field-wide">
<el-select
v-model="item.cloudSourceKeys"
class="text-input"
multiple
collapse-tags
collapse-tags-tooltip
placeholder="按顺序选择可用于履约的 cloud 账号"
@change="emit('handleCloudSourceKeysChanged')"
>
<el-option
v-for="opt in cloudtentaclesSourceOptions"
:key="opt.key"
:label="opt.label || opt.key"
:value="opt.key"
/>
</el-select>
<small class="field-help">执行时会从左到右尝试SKU 列表使用第一个账号加载</small>
</el-form-item>
<el-form-item label="cloud SKU ID">
<el-select
v-model="item.cloudSkuId"
class="text-input"
@change="emit('handleCloudSkuSelected', item.cloudSkuId)"
@visible-change="emit('handleCloudSkuDropdownVisible', $event)"
>
<el-option label="请选择 cloud SKU" :value="0" />
<el-option
v-for="sku in getCloudSkuOptions(item)"
:key="sku.id"
:label="formatCloudSkuOptionLabel(sku)"
:value="sku.id"
/>
</el-select>
<small class="field-help">先填内部 SKU / 商品名再选择会更容易匹配</small>
</el-form-item>
<el-form-item label="cloud SKU 名称" class="field-wide">
<el-input
:model-value="item.cloudSkuName"
class="text-input"
placeholder="选择 cloud SKU 后自动带出"
readonly
placeholder="商品名兜底,可选"
/>
</el-form-item>
<el-form-item label="虚拟号 VN Key">
<el-input class="text-input" model-value="1" readonly />
</el-form-item>
<el-form-item label="最低保留余额">
<el-input-number
v-model="item.minAssetReserve"
<el-form-item label="核销店铺 ID">
<el-input
:model-value="item.kuaishouConsumeShopId || '-'"
class="text-input"
:min="0"
controls-position="right"
readonly
/>
</el-form-item>
<el-form-item label="核销店铺名">
<el-input
:model-value="item.kuaishouConsumeShopName || '-'"
class="text-input"
readonly
/>
</el-form-item>
<el-form-item label="备注" class="field-wide">
<el-input
v-model="item.notes"
class="text-input textarea-input"
maxlength="400"
placeholder="绑定场景、客服注意事项等"
type="textarea"
/>
</el-form-item>
</el-form>
</el-card>
</details>
</div>
<el-form v-if="!isCollapsed" label-position="top" class="mapping-extra">
<el-form-item label="快手核销店铺" class="field-wide">
<el-select
v-model="item.kuaishouConsumeShopId"
class="text-input"
placeholder="请选择已配置 Cookie 的快手小店"
@change="emit('handleKuaishouConsumeShopSelected')"
>
<el-option label="请选择已配置 Cookie 的快手小店" value="" />
<el-option
v-for="shop in getKuaishouConsumeShopOptions()"
:key="shop.shopId"
:label="formatKuaishouConsumeShopOption(shop)"
:value="shop.shopId"
/>
</el-select>
<small class="field-help"
>来自"平台店铺 -> 快手小店核销"配置保存时会自动带出店铺名</small
>
</el-form-item>
<el-form-item label="核销店铺 ID">
<el-input :model-value="item.kuaishouConsumeShopId || '-'" class="text-input" readonly />
</el-form-item>
<el-form-item label="核销店铺名">
<el-input :model-value="item.kuaishouConsumeShopName || '-'" class="text-input" readonly />
</el-form-item>
<el-form-item label="备注" class="field-wide">
<el-input
v-model="item.notes"
class="text-input textarea-input"
maxlength="400"
placeholder="绑定场景、客服注意事项等"
type="textarea"
/>
</el-form-item>
</el-form>
<el-alert
v-if="validationState?.localId === item.localId"
:title="validationState?.message"
@@ -294,11 +328,7 @@ const emit = defineEmits<{
:closable="false"
/>
<div class="binding-actions">
<el-checkbox v-model="item.enabled">启用规则</el-checkbox>
<el-checkbox v-model="item.autoBuyEnabled">自动购买</el-checkbox>
<el-checkbox v-model="item.autoReturnNumberAfterDispatch">发货后退号</el-checkbox>
<el-checkbox v-model="item.autoConsumeAfterDispatch">发货后核销</el-checkbox>
<div v-if="!isCollapsed" class="binding-actions">
<el-button link type="danger" @click="emit('remove')">删除</el-button>
</div>
</el-card>