fix(admin): merge-candidates by SKU/family; config dialog merge block always shown; cfg badge adjacency
- 修正合并同名候选匹配:此前用名称 3 段组键(含工艺段),工艺写法不同的 同款(如 光板不打印 vs 单面印花(前印))全部漏配导致候选为空、合并同名 区块被 v-if 隐藏 —— 用户看到的"checkbox 和搜索框被吞"即此因。 改为 族归属精确匹配 或 款号(名称第 2 段,如 BRTF004)一致,全局跨分类; 区块常显,无候选时空态提示 - 修左树「N 个配置」徽标错位:good-name-col 改 flex 布局,徽标紧贴名称 - 验证:admin typecheck + 22/22 + 构建绿;DB 实测 BRTF004 光板链接候选池 恢复(单面印花(前印),同族 857)
This commit is contained in:
@@ -418,18 +418,26 @@ function findOgNodeById(rawId: string | number): any {
|
|||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 链接名的款号段(第 2 段,如 BRTF004);同款号 = 同款 */
|
||||||
|
function skuCodeOf(name: string | null | undefined): string {
|
||||||
|
return (name ?? '').split('-').map(s => s.trim())[1] ?? ''
|
||||||
|
}
|
||||||
|
|
||||||
function collectSiblings(ogNode: any): any[] {
|
function collectSiblings(ogNode: any): any[] {
|
||||||
const result: any[] = []
|
const result: any[] = []
|
||||||
|
const myFamily = ogNode.familyId ? String(ogNode.familyId) : null
|
||||||
|
const mySku = skuCodeOf(ogNode.goodName ?? ogNode.label)
|
||||||
function traverse(nodes: any[]) {
|
function traverse(nodes: any[]) {
|
||||||
for (const n of nodes) {
|
for (const n of nodes) {
|
||||||
// 全局同款链接(跨分类):合并同名按名称组键匹配,
|
// 全局同款链接(跨分类/跨工艺/跨物流):族归属精确匹配,或款号一致;
|
||||||
// 不局限于当前分类;含已配置的,勾选归族操作幂等
|
// 含已配置的,勾选归族操作幂等
|
||||||
if (
|
if (n.isOG && String(n.rawId) !== String(ogNode.rawId)) {
|
||||||
n.isOG &&
|
const nFamily = n.familyId ? String(n.familyId) : null
|
||||||
String(n.rawId) !== String(ogNode.rawId) &&
|
const nSku = skuCodeOf(n.goodName ?? n.label)
|
||||||
sameOriginGroup(ogNode.goodName ?? ogNode.label, n.goodName ?? n.label)
|
const same = (myFamily && nFamily && myFamily === nFamily)
|
||||||
) {
|
|| (Boolean(mySku) && Boolean(nSku) && mySku === nSku)
|
||||||
result.push(n)
|
|| (!mySku && sameOriginGroup(ogNode.goodName ?? ogNode.label, n.goodName ?? n.label))
|
||||||
|
if (same) result.push(n)
|
||||||
}
|
}
|
||||||
if (n.children?.length) traverse(n.children)
|
if (n.children?.length) traverse(n.children)
|
||||||
}
|
}
|
||||||
@@ -1566,7 +1574,14 @@ onMounted(() => loadAll())
|
|||||||
font-size: 13px; line-height: 1.4; cursor: pointer;
|
font-size: 13px; line-height: 1.4; cursor: pointer;
|
||||||
max-width: calc(100% - 40px);
|
max-width: calc(100% - 40px);
|
||||||
}
|
}
|
||||||
.good-name-col { flex: 1; min-width: 0; overflow: hidden; }
|
.good-name-col {
|
||||||
|
flex: 1; min-width: 0; overflow: hidden;
|
||||||
|
display: flex; align-items: center; gap: 6px;
|
||||||
|
}
|
||||||
|
.good-name-col > .el-tooltip__trigger {
|
||||||
|
min-width: 0; overflow: hidden;
|
||||||
|
display: inline-flex; align-items: center; gap: 4px;
|
||||||
|
}
|
||||||
.gv-merged-badge {
|
.gv-merged-badge {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
|
|||||||
@@ -135,13 +135,13 @@ async function handleSubmit() {
|
|||||||
<el-cascader v-if="mode === 'country' || !dropTarget" v-model="form.cascaderCategory" :options="categoryCascader as any" :props="{ checkStrictly: true }" placeholder="请选择分类" @change="onCascaderChange" style="width:100%" />
|
<el-cascader v-if="mode === 'country' || !dropTarget" v-model="form.cascaderCategory" :options="categoryCascader as any" :props="{ checkStrictly: true }" placeholder="请选择分类" @change="onCascaderChange" style="width:100%" />
|
||||||
<el-tag v-else>{{ dropTarget?.label }}</el-tag>
|
<el-tag v-else>{{ dropTarget?.label }}</el-tag>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="siblings.length" label="合并同名">
|
<el-form-item label="合并同名">
|
||||||
<div class="config-merge-box">
|
<div class="config-merge-box">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="mergeKeyword"
|
v-model="mergeKeyword"
|
||||||
size="small"
|
size="small"
|
||||||
clearable
|
clearable
|
||||||
placeholder="搜索同分类链接…"
|
placeholder="搜索同款链接(全局,含已配置)…"
|
||||||
style="margin-bottom:8px"
|
style="margin-bottom:8px"
|
||||||
/>
|
/>
|
||||||
<el-checkbox-group v-model="checkedIds">
|
<el-checkbox-group v-model="checkedIds">
|
||||||
@@ -149,7 +149,9 @@ async function handleSubmit() {
|
|||||||
{{ s.goodName }}<template v-if="s.goodPrice"> · ¥{{ s.goodPrice }}</template>
|
{{ s.goodName }}<template v-if="s.goodPrice"> · ¥{{ s.goodPrice }}</template>
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
<div v-if="!filteredSiblings.length" class="config-merge-empty">没有匹配的链接</div>
|
<div v-if="!filteredSiblings.length" class="config-merge-empty">
|
||||||
|
{{ siblings.length ? '没有匹配的链接' : '未找到同款链接(同款号或同族)' }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="图片">
|
<el-form-item label="图片">
|
||||||
|
|||||||
@@ -154,8 +154,9 @@ pnpm --filter @inkreach/api backfill:product-families
|
|||||||
归并(同组合取最低价,「链接数」列显示来源数,人工价带标签),无需额外请求;
|
归并(同组合取最低价,「链接数」列显示来源数,人工价带标签),无需额外请求;
|
||||||
- 编辑弹窗商品「名称」预填解析后的「品名 型号」,保存即采用解析名;右侧原产品库与配置弹窗
|
- 编辑弹窗商品「名称」预填解析后的「品名 型号」,保存即采用解析名;右侧原产品库与配置弹窗
|
||||||
始终显示**原始链接名**;
|
始终显示**原始链接名**;
|
||||||
- 配置弹窗「合并同名」候选为**全局同款链接**(按名称 3 段组键跨分类匹配,
|
- 配置弹窗「合并同名」区块**常显**,候选为**全局同款链接**:按 链接款号(名称第 2 段,
|
||||||
含已配置链接),支持搜索过滤。
|
如 BRTF004)或族归属跨分类匹配,同款不同工艺/物流/仓库均在列(含已配置链接),
|
||||||
|
支持搜索过滤;无候选时显示空态提示。
|
||||||
- 右树徽标语义(**族感知**,公开契约一族一款):**已配置 N**(绿,本链接已配置为官网商品,
|
- 右树徽标语义(**族感知**,公开契约一族一款):**已配置 N**(绿,本链接已配置为官网商品,
|
||||||
N=国数)/ **已配置**(绿,本链接未单独建商品但所属族已配置)/ **成族**(蓝,已归族且全族
|
N=国数)/ **已配置**(绿,本链接未单独建商品但所属族已配置)/ **成族**(蓝,已归族且全族
|
||||||
未配置)/ **未配置**(橙);「定位到官网商品」在自身或同族已配置时出现(按 主链接 → 同族 →
|
未配置)/ **未配置**(橙);「定位到官网商品」在自身或同族已配置时出现(按 主链接 → 同族 →
|
||||||
|
|||||||
Reference in New Issue
Block a user