feat: 横向对比改为会员阶梯式排列
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 4s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Has been cancelled
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 4s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Has been cancelled
- 列按套装可做配方数从少到多排(小套装左,大套装右) - 行按可用套装数从多到少排(所有套装都能做的在上,独占的在下) - 形成左上到右下的阶梯视觉效果 - 有值格子绿色背景,无值格子灰色,区分更明显 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -74,7 +74,7 @@ export function useKitCost() {
|
|||||||
.sort((a, b) => a.name.localeCompare(b.name, 'zh'))
|
.sort((a, b) => a.name.localeCompare(b.name, 'zh'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build full analysis for all kits
|
// Build full analysis for all kits, sorted by recipe count ascending (fewest recipes first)
|
||||||
const kitAnalysis = computed(() => {
|
const kitAnalysis = computed(() => {
|
||||||
return KITS.map(kit => {
|
return KITS.map(kit => {
|
||||||
const perDrop = calcKitPerDrop(kit)
|
const perDrop = calcKitPerDrop(kit)
|
||||||
@@ -85,12 +85,17 @@ export function useKitCost() {
|
|||||||
recipes,
|
recipes,
|
||||||
recipeCount: recipes.length,
|
recipeCount: recipes.length,
|
||||||
}
|
}
|
||||||
})
|
}).sort((a, b) => a.recipeCount - b.recipeCount)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Cross-kit comparison: all recipes that at least one kit can make
|
// Cross-kit comparison: membership-tier style
|
||||||
|
// Columns: kits ordered by recipe count (fewest→most, from kitAnalysis)
|
||||||
|
// Rows: recipes available to most kits at top, exclusive recipes at bottom (staircase pattern)
|
||||||
const crossComparison = computed(() => {
|
const crossComparison = computed(() => {
|
||||||
const analysis = kitAnalysis.value
|
const analysis = kitAnalysis.value
|
||||||
|
// Kit order for staircase: index in sorted analysis (0 = smallest kit)
|
||||||
|
const kitOrder = analysis.map(ka => ka.id)
|
||||||
|
|
||||||
const allRecipeIds = new Set()
|
const allRecipeIds = new Set()
|
||||||
for (const ka of analysis) {
|
for (const ka of analysis) {
|
||||||
for (const r of ka.recipes) allRecipeIds.add(r._id)
|
for (const r of ka.recipes) allRecipeIds.add(r._id)
|
||||||
@@ -98,16 +103,21 @@ export function useKitCost() {
|
|||||||
|
|
||||||
const rows = []
|
const rows = []
|
||||||
for (const id of allRecipeIds) {
|
for (const id of allRecipeIds) {
|
||||||
// Find recipe info from any kit that has it
|
|
||||||
let recipe = null
|
let recipe = null
|
||||||
const costs = {}
|
const costs = {}
|
||||||
for (const ka of analysis) {
|
let availCount = 0
|
||||||
|
// Track which kit columns have this recipe (by index in sorted order)
|
||||||
|
let smallestKitIdx = kitOrder.length
|
||||||
|
for (let i = 0; i < analysis.length; i++) {
|
||||||
|
const ka = analysis[i]
|
||||||
const found = ka.recipes.find(r => r._id === id)
|
const found = ka.recipes.find(r => r._id === id)
|
||||||
if (found) {
|
if (found) {
|
||||||
if (!recipe) recipe = found
|
if (!recipe) recipe = found
|
||||||
costs[ka.id] = found.kitCost
|
costs[ka.id] = found.kitCost
|
||||||
|
availCount++
|
||||||
|
if (i < smallestKitIdx) smallestKitIdx = i
|
||||||
} else {
|
} else {
|
||||||
costs[ka.id] = null // kit can't make this recipe
|
costs[ka.id] = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rows.push({
|
rows.push({
|
||||||
@@ -116,14 +126,15 @@ export function useKitCost() {
|
|||||||
tags: recipe.tags,
|
tags: recipe.tags,
|
||||||
ingredients: recipe.ingredients,
|
ingredients: recipe.ingredients,
|
||||||
originalCost: recipe.originalCost,
|
originalCost: recipe.originalCost,
|
||||||
costs, // { aroma: 12.3, family: null, home3988: 10.8, full: 9.5 }
|
costs,
|
||||||
|
availCount,
|
||||||
|
smallestKitIdx,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Sort by number of kits that can make the recipe (fewest first), then by name
|
// Staircase sort: most available first, then by smallest kit that has it, then by name
|
||||||
rows.sort((a, b) => {
|
rows.sort((a, b) => {
|
||||||
const aCount = Object.values(a.costs).filter(v => v != null).length
|
if (a.availCount !== b.availCount) return b.availCount - a.availCount
|
||||||
const bCount = Object.values(b.costs).filter(v => v != null).length
|
if (a.smallestKitIdx !== b.smallestKitIdx) return a.smallestKitIdx - b.smallestKitIdx
|
||||||
if (aCount !== bCount) return aCount - bCount
|
|
||||||
return a.name.localeCompare(b.name, 'zh')
|
return a.name.localeCompare(b.name, 'zh')
|
||||||
})
|
})
|
||||||
return rows
|
return rows
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="row in crossComparison" :key="row.id">
|
<tr v-for="row in crossComparison" :key="row.id">
|
||||||
<td class="td-name">{{ row.name }}</td>
|
<td class="td-name">{{ row.name }}</td>
|
||||||
<td v-for="ka in kitAnalysis" :key="ka.id" class="td-kit-cost">
|
<td v-for="ka in kitAnalysis" :key="ka.id" :class="row.costs[ka.id] != null ? 'td-kit-available' : 'td-kit-na'">
|
||||||
<template v-if="row.costs[ka.id] != null">{{ fmtPrice(row.costs[ka.id]) }}</template>
|
<template v-if="row.costs[ka.id] != null">{{ fmtPrice(row.costs[ka.id]) }}</template>
|
||||||
<template v-else><span class="na">—</span></template>
|
<template v-else><span class="na">—</span></template>
|
||||||
</td>
|
</td>
|
||||||
@@ -404,8 +404,9 @@ async function exportExcel(mode) {
|
|||||||
.td-cost.original { color: #999; font-weight: 400; }
|
.td-cost.original { color: #999; font-weight: 400; }
|
||||||
.td-profit { font-weight: 600; color: #4a9d7e; }
|
.td-profit { font-weight: 600; color: #4a9d7e; }
|
||||||
.td-profit.negative { color: #ef5350; }
|
.td-profit.negative { color: #ef5350; }
|
||||||
.td-kit-cost { font-weight: 500; }
|
.td-kit-available { font-weight: 500; color: #4a9d7e; background: #f0faf5; }
|
||||||
.na { color: #ccc; }
|
.td-kit-na { background: #fafafa; }
|
||||||
|
.na { color: #ddd; }
|
||||||
|
|
||||||
.price-input-wrap {
|
.price-input-wrap {
|
||||||
display: inline-flex; align-items: center; gap: 2px; font-size: 13px; color: #3e3a44;
|
display: inline-flex; align-items: center; gap: 2px; font-size: 13px; color: #3e3a44;
|
||||||
|
|||||||
Reference in New Issue
Block a user