履约配置和 库存界面ui优化

This commit is contained in:
yml2213
2026-04-23 12:20:11 +08:00
parent 512b58856f
commit 2a05a68959
2 changed files with 378 additions and 283 deletions
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref } from 'vue'
import { computed, nextTick, onMounted, reactive, ref } from 'vue'
import { showSuccess } from '@/lib/feedback'
import {
@@ -83,6 +83,22 @@ const lookupForm = reactive({
platformOrderId: '',
})
const ruleMetrics = computed(() => {
const completedCount = bindings.value.filter(isEditableBindingComplete).length
const manualCount = bindings.value.filter((item) => item.profileKey.trim() === 'manual_review').length
const assistedCount = bindings.value.filter((item) => item.profileKey.trim() === 'tencent_claim_assisted').length
return {
totalCount: bindings.value.length,
completedCount,
draftCount: Math.max(bindings.value.length - completedCount, 0),
manualCount,
assistedCount,
}
})
const pendingObservedCount = computed(() => observedProducts.value.filter((item) => !item.configured).length)
type ImportableProductCandidate = Pick<
AdminObservedProductItem,
'provider' | 'platform' | 'shopId' | 'externalSkuCode' | 'externalItemId' | 'externalSkuName'
@@ -449,56 +465,56 @@ onMounted(loadConfigs)
<template>
<section class="admin-panel">
<header class="panel-header">
<div>
<div class="panel-header-copy">
<h1>履约配置</h1>
<p>把外部商品识别成内部履约 SKU再决定绑定库存还是转人工保存后立即同步到后端规则表</p>
<p>把外部商品统一映射到内部履约 SKU并指定最终走自动半自动还是人工处理</p>
</div>
<div class="header-tags">
<span class="header-tag">外部条件命中 1 个即可</span>
<span class="header-tag">同类商品尽量复用同一内部 SKU</span>
<span class="header-tag">保存后立即生效</span>
</div>
<div class="header-actions">
<el-button round @click="loadConfigs">刷新</el-button>
<el-button round type="primary" @click="addBinding">新增规则</el-button>
</div>
</header>
<div v-if="!hasAdminRole('admin')" class="empty-block">仅管理员可以维护履约配置</div>
<template v-else>
<section class="meta-card">
<div class="meta-line">
<span class="meta-label">配置文件</span>
<section class="overview-card">
<div class="overview-file">
<span class="overview-label">配置文件</span>
<code>{{ filePath || '-' }}</code>
</div>
<div class="meta-line">
<span class="meta-label">说明</span>
<span>每条规则同时定义外部商品匹配条件内部履约 SKU / 履约方式未命中的商品会直接忽略不再继续走后续履约动作</span>
<div class="overview-stats">
<article class="overview-stat">
<span>规则总数</span>
<strong>{{ ruleMetrics.totalCount }}</strong>
</article>
<article class="overview-stat">
<span>完整规则</span>
<strong>{{ ruleMetrics.completedCount }}</strong>
</article>
<article class="overview-stat">
<span>待完善</span>
<strong>{{ ruleMetrics.draftCount }}</strong>
</article>
<article class="overview-stat">
<span>待补商品</span>
<strong>{{ pendingObservedCount }}</strong>
</article>
<article class="overview-stat">
<span>已识别商品</span>
<strong>{{ observedProducts.length }}</strong>
</article>
</div>
<div class="meta-line">
<span class="meta-label">字段怎么理解</span>
<div class="hint-grid">
<div class="hint-item">
<strong>外部 SKU 编码 / 外部商品 ID / 外部商品名</strong>
<span>这是平台或店铺回传的原始商品标识用来识别你卖的是哪一种商品</span>
</div>
<div class="hint-item">
<strong>内部履约 SKU</strong>
<span>这是你系统里的统一商品编码后续库存绑定自动履约人工处理都靠它继续往下走</span>
</div>
<div class="hint-item">
<strong>内部商品名</strong>
<span>主要给后台展示看建议填你们内部真正认识的商品名</span>
</div>
<div class="hint-item">
<strong>履约方式</strong>
<span>`腾讯领取兑换` 表示用户确认后自动兑换`腾讯领取兑换(半自动+人工)` 表示用户先登录客服确认角色后再自动兑换`人工发货` 表示直接落到人工处理</span>
</div>
</div>
</div>
<div class="meta-line">
<span class="meta-label">内部履约 SKU 怎么填</span>
<div class="sku-guide">
<span>推荐用你们内部统一命名不要直接抄店铺里的外部 SKU</span>
<span>建议格式`游戏或业务_商品类型_规格`例如 `delta_havabi_500``dnf_cdk_blackdiamond``manual_peripheral_common`</span>
<span>原则是同一种履约商品无论来自哪个平台哪个店铺都尽量落到同一个内部履约 SKU</span>
</div>
<div class="overview-notes">
<span class="overview-note">外部 SKU / 商品 ID / 商品名命中任一即可</span>
<span class="overview-note">内部 SKU 决定库存绑定与履约链路</span>
<span class="overview-note">同类商品跨平台尽量共用同一内部 SKU</span>
</div>
</section>
@@ -506,126 +522,24 @@ onMounted(loadConfigs)
<div v-if="loading" class="empty-block">履约配置加载中</div>
<template v-else>
<section class="table-card">
<div class="section-title-row">
<div>
<h3>手动查询订单商品</h3>
<p>新商品还没配规则时可以按店铺 ID 和平台订单号临时补查不需要恢复全量订单详情补查</p>
</div>
</div>
<div class="lookup-grid">
<label class="field-block">
<span>渠道</span>
<input class="text-input" value="agiso" disabled />
</label>
<label class="field-block">
<span>平台</span>
<input class="text-input" value="xianyu" disabled />
</label>
<label class="field-block">
<span>店铺 ID</span>
<input
v-model="lookupForm.shopId"
class="text-input"
placeholder="例如 252609"
@keydown.enter.prevent="lookupOrderProducts"
/>
</label>
<label class="field-block">
<span>平台订单号</span>
<input
v-model="lookupForm.platformOrderId"
class="text-input"
placeholder="输入需要补查的订单号"
@keydown.enter.prevent="lookupOrderProducts"
/>
</label>
</div>
<div class="lookup-actions">
<el-button :loading="lookupLoading" round type="primary" @click="lookupOrderProducts">查询订单商品</el-button>
<span class="cell-subtle">当前仅支持 Agiso 咸鱼订单查询结果只用于补配置不会写入订单库</span>
</div>
<p v-if="lookupErrorMessage" class="error-copy lookup-error">{{ lookupErrorMessage }}</p>
<div v-else-if="lookupLoading" class="empty-inline">正在查询订单详情</div>
<template v-else-if="lookupResult">
<div class="lookup-summary">
<div class="lookup-summary-item">
<span class="meta-label">订单</span>
<strong>{{ lookupResult.order.platformOrderId }}</strong>
<span class="cell-subtle">{{ lookupResult.order.shopName || lookupResult.order.shopId || '-' }}</span>
</div>
<div class="lookup-summary-item">
<span class="meta-label">买家</span>
<strong>{{ lookupResult.order.buyerName || '-' }}</strong>
<span class="cell-subtle">支付时间{{ formatAdminDateTime(lookupResult.order.paidAt) }}</span>
</div>
<div class="lookup-summary-item">
<span class="meta-label">金额</span>
<strong>{{ lookupResult.order.totalAmount || '0.00' }}</strong>
<span class="cell-subtle">补查结果{{ lookupResult.order.enriched ? '已补全' : lookupResult.order.enrichReason || '已返回' }}</span>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>外部商品</th>
<th>数量</th>
<th>识别结果</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in lookupResult.items" :key="item.lineId">
<td>
<div class="cell-stack">
<strong>{{ item.externalSkuName || item.itemTitle || item.externalSkuCode || item.externalItemId || '-' }}</strong>
<span class="cell-subtle">SKU: {{ item.externalSkuCode || '-' }}</span>
<span class="cell-subtle">ItemId: {{ item.externalItemId || '-' }}</span>
</div>
</td>
<td>{{ item.quantity }}</td>
<td>
<div class="cell-stack">
<strong>{{ item.configured ? '已配置' : '未配置' }}</strong>
<span v-if="item.matchedBinding" class="cell-subtle">
{{ item.matchedBinding.skuName || item.matchedBinding.skuCode || '-' }} / {{ item.matchedBinding.profileKey || '-' }}
</span>
<span v-else class="cell-subtle">可直接导入成新规则草稿</span>
</div>
</td>
<td>
<el-button
v-if="!item.configured && !isLookupProductImported(item.lineId)"
link
type="primary"
@click="importLookupProduct(item)"
>
导入到规则
</el-button>
<span v-else-if="item.configured" class="cell-subtle">已存在</span>
<span v-else class="cell-subtle">已导入草稿</span>
</td>
</tr>
</tbody>
</table>
</template>
</section>
<section class="table-card">
<div class="section-title-row">
<section class="table-card table-card--dense">
<div class="section-title-row section-title-row--tight">
<div>
<h3>已配置规则</h3>
<p>建议至少填写平台店铺外部 SKU 编码或外部商品名再指定内部履约 SKU 和履约档案</p>
<p>正式规则集中维护在这里新补查到的商品也会导入到这个区域继续补全</p>
</div>
<div class="section-header-tools">
<div class="section-mini-stats">
<span class="mini-stat-chip">完整 {{ ruleMetrics.completedCount }}</span>
<span class="mini-stat-chip">待完善 {{ ruleMetrics.draftCount }}</span>
<span class="mini-stat-chip">人工 {{ ruleMetrics.manualCount }}</span>
<span class="mini-stat-chip">半自动 {{ ruleMetrics.assistedCount }}</span>
</div>
<div class="section-action-group">
<el-button round @click="addBinding">新增规则</el-button>
<el-button :loading="saving" round type="primary" @click="saveConfigs">保存配置</el-button>
</div>
</div>
<el-button :loading="saving" round type="primary" @click="saveConfigs">保存配置</el-button>
</div>
<p v-if="validationState?.message" class="validation-banner">{{ validationState.message }}</p>
@@ -767,11 +681,131 @@ onMounted(loadConfigs)
</div>
</section>
<section class="table-card">
<div class="section-title-row">
<section class="table-card table-card--dense">
<div class="section-title-row section-title-row--tight">
<div>
<h3>手动补查订单商品</h3>
<p>新商品还没配规则时可以按店铺 ID 和平台订单号临时补查查完直接导入规则草稿</p>
</div>
<span class="section-note-chip">仅支持 Agiso 咸鱼订单</span>
</div>
<div class="lookup-toolbar">
<label class="field-block">
<span>渠道</span>
<input class="text-input" value="agiso" disabled />
</label>
<label class="field-block">
<span>平台</span>
<input class="text-input" value="xianyu" disabled />
</label>
<label class="field-block">
<span>店铺 ID</span>
<input
v-model="lookupForm.shopId"
class="text-input"
placeholder="例如 252609"
@keydown.enter.prevent="lookupOrderProducts"
/>
</label>
<label class="field-block">
<span>平台订单号</span>
<input
v-model="lookupForm.platformOrderId"
class="text-input"
placeholder="输入需要补查的订单号"
@keydown.enter.prevent="lookupOrderProducts"
/>
</label>
<div class="field-block field-block--action">
<span>操作</span>
<el-button :loading="lookupLoading" round type="primary" @click="lookupOrderProducts">查询订单商品</el-button>
</div>
</div>
<p class="toolbar-hint">查询结果只用于补配置不会写入订单库也不会影响现有订单数据</p>
<p v-if="lookupErrorMessage" class="error-copy lookup-error">{{ lookupErrorMessage }}</p>
<div v-else-if="lookupLoading" class="empty-inline">正在查询订单详情</div>
<template v-else-if="lookupResult">
<div class="lookup-summary">
<div class="lookup-summary-item">
<span class="summary-label">订单</span>
<strong>{{ lookupResult.order.platformOrderId }}</strong>
<span class="cell-subtle">{{ lookupResult.order.shopName || lookupResult.order.shopId || '-' }}</span>
</div>
<div class="lookup-summary-item">
<span class="summary-label">买家</span>
<strong>{{ lookupResult.order.buyerName || '-' }}</strong>
<span class="cell-subtle">支付时间{{ formatAdminDateTime(lookupResult.order.paidAt) }}</span>
</div>
<div class="lookup-summary-item">
<span class="summary-label">金额</span>
<strong>{{ lookupResult.order.totalAmount || '0.00' }}</strong>
<span class="cell-subtle">补查结果{{ lookupResult.order.enriched ? '已补全' : lookupResult.order.enrichReason || '已返回' }}</span>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>外部商品</th>
<th>数量</th>
<th>识别结果</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in lookupResult.items" :key="item.lineId">
<td>
<div class="cell-stack">
<strong>{{ item.externalSkuName || item.itemTitle || item.externalSkuCode || item.externalItemId || '-' }}</strong>
<span class="cell-subtle">SKU: {{ item.externalSkuCode || '-' }}</span>
<span class="cell-subtle">ItemId: {{ item.externalItemId || '-' }}</span>
</div>
</td>
<td>{{ item.quantity }}</td>
<td>
<div class="cell-stack">
<strong>{{ item.configured ? '已配置' : '未配置' }}</strong>
<span v-if="item.matchedBinding" class="cell-subtle">
{{ item.matchedBinding.skuName || item.matchedBinding.skuCode || '-' }} / {{ item.matchedBinding.profileKey || '-' }}
</span>
<span v-else class="cell-subtle">可直接导入成新规则草稿</span>
</div>
</td>
<td>
<el-button
v-if="!item.configured && !isLookupProductImported(item.lineId)"
link
type="primary"
@click="importLookupProduct(item)"
>
导入到规则
</el-button>
<span v-else-if="item.configured" class="cell-subtle">已存在</span>
<span v-else class="cell-subtle">已导入草稿</span>
</td>
</tr>
</tbody>
</table>
</template>
</section>
<section class="table-card table-card--dense">
<div class="section-title-row section-title-row--tight">
<div>
<h3>近期识别到的外部商品</h3>
<p>从已入库订单里提取的外部商品信息可一键导入规则后补充内部履约 SKU</p>
<p>从已入库订单里提取的外部商品信息优先处理未配置商品导入后再补齐内部 SKU</p>
</div>
<div class="section-mini-stats">
<span class="mini-stat-chip">待补 {{ pendingObservedCount }}</span>
<span class="mini-stat-chip">总计 {{ observedProducts.length }}</span>
</div>
</div>
@@ -830,7 +864,7 @@ onMounted(loadConfigs)
<style scoped>
.admin-panel {
display: grid;
gap: 16px;
gap: 12px;
}
.panel-header,
@@ -849,92 +883,178 @@ onMounted(loadConfigs)
.panel-header p,
.section-title-row p {
margin: 8px 0 0;
margin: 6px 0 0;
color: #64748b;
}
.panel-header {
align-items: flex-start;
flex-wrap: wrap;
}
.panel-header-copy {
min-width: 280px;
flex: 1;
}
.header-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.header-tag,
.section-note-chip,
.mini-stat-chip,
.summary-label,
.overview-label {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 0 10px;
border-radius: 999px;
background: rgba(37, 89, 167, 0.08);
border: 1px solid rgba(37, 89, 167, 0.12);
color: #2559a7;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
}
.header-actions {
display: flex;
gap: 10px;
}
.meta-card,
.overview-card,
.table-card,
.empty-block,
.error-copy {
padding: 18px;
padding: 16px;
border-radius: 20px;
background: rgba(255, 255, 255, 0.94);
border: 1px solid rgba(86, 108, 138, 0.1);
}
.meta-card {
display: grid;
gap: 10px;
.table-card--dense {
padding: 14px 16px 16px;
}
.hint-grid,
.sku-guide {
display: grid;
gap: 8px;
}
.lookup-grid,
.lookup-summary {
.overview-card {
display: grid;
gap: 12px;
}
.hint-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.lookup-grid,
.lookup-summary {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.hint-item {
display: grid;
gap: 4px;
padding: 10px 12px;
border-radius: 14px;
background: #f8fafc;
border: 1px solid rgba(148, 163, 184, 0.14);
}
.hint-item strong {
color: #1d3555;
font-size: 13px;
}
.hint-item span,
.sku-guide span {
color: #64748b;
font-size: 13px;
line-height: 1.6;
}
.meta-line {
.overview-file {
display: grid;
gap: 6px;
}
.lookup-actions {
margin-top: 14px;
.overview-file code {
display: inline-flex;
width: 100%;
max-width: 100%;
padding: 8px 12px;
border-radius: 14px;
background: rgba(15, 23, 42, 0.05);
color: #1d3555;
word-break: break-all;
}
.overview-stats {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px;
}
.overview-stat {
display: grid;
gap: 4px;
padding: 12px 14px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid rgba(148, 163, 184, 0.14);
}
.overview-stat span {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.overview-stat strong {
color: #1d3555;
font-size: 22px;
line-height: 1.1;
}
.overview-notes {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.overview-note {
display: inline-flex;
align-items: center;
justify-content: space-between;
min-height: 32px;
padding: 0 12px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.04);
color: #475467;
font-size: 12px;
font-weight: 600;
}
.section-title-row--tight {
align-items: center;
}
.section-header-tools {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: flex-end;
align-items: center;
}
.section-mini-stats,
.section-action-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.lookup-toolbar,
.lookup-summary {
display: grid;
gap: 12px;
}
.lookup-toolbar {
grid-template-columns: 96px 110px minmax(140px, 1fr) minmax(180px, 1.2fr) 136px;
align-items: end;
}
.field-block--action :deep(.el-button) {
width: 100%;
}
.toolbar-hint {
margin: 10px 0 0;
color: #64748b;
font-size: 12px;
}
.lookup-summary {
margin-top: 16px;
margin-top: 14px;
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.lookup-summary-item {
display: grid;
gap: 4px;
gap: 6px;
padding: 12px 14px;
border-radius: 16px;
background: #f8fafc;
@@ -945,16 +1065,10 @@ onMounted(loadConfigs)
color: #1d3555;
}
.meta-label {
color: #64748b;
font-size: 12px;
font-weight: 700;
}
.binding-card {
margin-top: 14px;
padding: 16px;
border-radius: 18px;
margin-top: 12px;
padding: 14px;
border-radius: 16px;
background: #f8fafc;
border: 1px solid rgba(148, 163, 184, 0.18);
}
@@ -967,9 +1081,9 @@ onMounted(loadConfigs)
.binding-header {
display: flex;
justify-content: space-between;
gap: 16px;
gap: 12px;
align-items: flex-start;
margin-bottom: 12px;
margin-bottom: 10px;
}
.binding-summary {
@@ -1011,7 +1125,7 @@ onMounted(loadConfigs)
background: rgba(255, 255, 255, 0.96);
color: #1d3555;
border-radius: 999px;
padding: 8px 14px;
padding: 7px 12px;
font: inherit;
cursor: pointer;
}
@@ -1026,12 +1140,12 @@ onMounted(loadConfigs)
.binding-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
gap: 10px 12px;
}
.validation-banner {
margin: 14px 0 0;
padding: 12px 14px;
margin: 12px 0 0;
padding: 10px 12px;
border-radius: 14px;
background: #fff4f2;
border: 1px solid rgba(244, 63, 94, 0.18);
@@ -1041,7 +1155,7 @@ onMounted(loadConfigs)
.field-block {
display: grid;
gap: 8px;
gap: 6px;
}
.field-block span {
@@ -1062,8 +1176,8 @@ onMounted(loadConfigs)
.text-input {
width: 100%;
min-height: 42px;
padding: 0 14px;
min-height: 40px;
padding: 0 12px;
border-radius: 14px;
border: 1px solid rgba(86, 108, 138, 0.18);
background: rgba(255, 255, 255, 0.96);
@@ -1081,14 +1195,14 @@ onMounted(loadConfigs)
}
.binding-inline-error {
margin: 12px 0 0;
margin: 10px 0 0;
color: #b42318;
font-size: 13px;
font-weight: 600;
}
.binding-actions {
margin-top: 12px;
margin-top: 10px;
display: flex;
justify-content: space-between;
gap: 12px;
@@ -1144,15 +1258,15 @@ onMounted(loadConfigs)
@media (max-width: 980px) {
.panel-header,
.section-title-row,
.lookup-actions,
.section-header-tools,
.binding-actions,
.binding-header {
flex-direction: column;
align-items: stretch;
}
.hint-grid,
.lookup-grid,
.overview-stats,
.lookup-toolbar,
.lookup-summary,
.binding-grid {
grid-template-columns: 1fr;
@@ -461,35 +461,22 @@ onMounted(async () => {
<template>
<section class="admin-panel">
<header class="panel-header">
<div>
<div class="panel-header-copy">
<h1>库存管理</h1>
<p>库存项现在按内部履约 SKU + 凭据类型入池当前三角洲行动腾讯兑换码自动领取会优先使用 `tencent_code` 类型库存</p>
<p>按内部履约 SKU 和凭据类型管理库存预占和发放</p>
</div>
<div class="header-tags">
<span class="header-tag"> SKU + 凭据类型入池</span>
<span class="header-tag">腾讯兑换优先使用 `tencent_code`</span>
<span class="header-tag">命中库存后自动流转或转人工</span>
</div>
</header>
<section class="guide-card">
<article class="guide-item">
<span class="guide-label">1. 商品识别</span>
<h2>先识别成内部履约 SKU</h2>
<p>平台商品不会直接配库存订单先在商品识别与履约配置里映射成内部履约 SKU再来到这里找库存</p>
</article>
<article class="guide-item">
<span class="guide-label">2. 库存预占</span>
<h2>再按凭据类型找库存项</h2>
<p>`tencent_claim_redeem` `tencent_claim_assisted` 当前都会优先查找 `tencent_code`适合三角洲行动这类腾讯兑换码领取场景以后扩展别的履约档案时也会按各自 requirement 匹配</p>
</article>
<article class="guide-item">
<span class="guide-label">3. 履约结果</span>
<h2>自动绑定或转人工</h2>
<p>命中库存后任务会进入预占 / 发放链路配置为 `tencent_claim_assisted` 时会进入用户登录 + 客服确认 + 后端自动兑换配置走 `manual_review` 会落到人工处理</p>
</article>
</section>
<section class="filter-card">
<div class="card-head">
<div>
<h2>查询库存池</h2>
<p>按内部履约 SKU凭据类型批次和状态查看当前库存</p>
<p>按内部履约 SKU凭据类型批次和状态快速定位库存</p>
</div>
</div>
<div class="form-grid">
@@ -516,7 +503,7 @@ onMounted(async () => {
<div class="card-head">
<div>
<h2>入库与导入</h2>
<p>这里填写的是内部履约 SKU不是平台侧 `sku_code`内部 SKU 支持中文命名同一类商品建议长期复用同一个名字这样订单才能稳定共用库存池</p>
<p>填写内部履约 SKU不是平台侧 `sku_code`建议同类商品长期复用同一个内部名称</p>
</div>
</div>
@@ -770,26 +757,24 @@ onMounted(async () => {
<style scoped>
.admin-panel {
display: grid;
gap: 16px;
gap: 14px;
}
.panel-header h1,
.card-head h2,
.guide-item h2 {
.pool-title {
margin: 0;
color: #1d3555;
}
.panel-header p,
.card-head p,
.guide-item p,
.toolbar-copy,
.section-tip {
margin: 8px 0 0;
color: #64748b;
}
.guide-card,
.filter-card,
.import-card,
.table-card,
@@ -801,33 +786,38 @@ onMounted(async () => {
border: 1px solid rgba(86, 108, 138, 0.1);
}
.guide-card {
.panel-header {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
gap: 10px;
}
.guide-item {
padding: 16px;
border-radius: 18px;
background: linear-gradient(180deg, rgba(244, 248, 255, 0.96), rgba(255, 255, 255, 0.96));
border: 1px solid rgba(86, 108, 138, 0.08);
.panel-header-copy p {
margin-top: 6px;
}
.guide-label,
.helper-title {
.header-tags {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.header-tag,
.helper-title,
.summary-label,
.pool-total {
display: inline-flex;
margin-bottom: 10px;
align-items: center;
min-height: 32px;
padding: 0 12px;
border-radius: 999px;
background: rgba(37, 89, 167, 0.08);
border: 1px solid rgba(37, 89, 167, 0.12);
color: #2559a7;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
}
.guide-item p {
line-height: 1.6;
}
.card-head {
display: flex;
justify-content: space-between;
@@ -975,14 +965,6 @@ onMounted(async () => {
gap: 8px;
}
.summary-label,
.pool-total {
color: #2559a7;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.04em;
}
.summary-value {
font-size: 30px;
line-height: 1;
@@ -1176,7 +1158,6 @@ onMounted(async () => {
@media (max-width: 1180px) {
.summary-grid,
.pool-grid,
.guide-card,
.form-grid,
.helper-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -1186,12 +1167,12 @@ onMounted(async () => {
@media (max-width: 860px) {
.summary-grid,
.pool-grid,
.guide-card,
.form-grid,
.helper-grid {
grid-template-columns: minmax(0, 1fr);
}
.header-tags,
.single-row,
.action-row,
.sku-helper-bar {