From ccd3607e350d001026e9169d925576b9017db0f3 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 23:18:15 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A8=AA=E5=90=91=E5=AF=B9=E6=AF=94?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=BC=9A=E5=91=98=E9=98=B6=E6=A2=AF=E5=BC=8F?= =?UTF-8?q?=E6=8E=92=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 列按套装可做配方数从少到多排(小套装左,大套装右) - 行按可用套装数从多到少排(所有套装都能做的在上,独占的在下) - 形成左上到右下的阶梯视觉效果 - 有值格子绿色背景,无值格子灰色,区分更明显 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/composables/useKitCost.js | 33 +++++++++++++++++--------- frontend/src/views/KitExport.vue | 7 +++--- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/frontend/src/composables/useKitCost.js b/frontend/src/composables/useKitCost.js index cd63ef0..adfc194 100644 --- a/frontend/src/composables/useKitCost.js +++ b/frontend/src/composables/useKitCost.js @@ -74,7 +74,7 @@ export function useKitCost() { .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(() => { return KITS.map(kit => { const perDrop = calcKitPerDrop(kit) @@ -85,12 +85,17 @@ export function useKitCost() { recipes, 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 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() for (const ka of analysis) { for (const r of ka.recipes) allRecipeIds.add(r._id) @@ -98,16 +103,21 @@ export function useKitCost() { const rows = [] for (const id of allRecipeIds) { - // Find recipe info from any kit that has it let recipe = null 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) if (found) { if (!recipe) recipe = found costs[ka.id] = found.kitCost + availCount++ + if (i < smallestKitIdx) smallestKitIdx = i } else { - costs[ka.id] = null // kit can't make this recipe + costs[ka.id] = null } } rows.push({ @@ -116,14 +126,15 @@ export function useKitCost() { tags: recipe.tags, ingredients: recipe.ingredients, 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) => { - const aCount = Object.values(a.costs).filter(v => v != null).length - const bCount = Object.values(b.costs).filter(v => v != null).length - if (aCount !== bCount) return aCount - bCount + if (a.availCount !== b.availCount) return b.availCount - a.availCount + if (a.smallestKitIdx !== b.smallestKitIdx) return a.smallestKitIdx - b.smallestKitIdx return a.name.localeCompare(b.name, 'zh') }) return rows diff --git a/frontend/src/views/KitExport.vue b/frontend/src/views/KitExport.vue index 3536af5..5fda9d8 100644 --- a/frontend/src/views/KitExport.vue +++ b/frontend/src/views/KitExport.vue @@ -84,7 +84,7 @@ {{ row.name }} - + @@ -404,8 +404,9 @@ async function exportExcel(mode) { .td-cost.original { color: #999; font-weight: 400; } .td-profit { font-weight: 600; color: #4a9d7e; } .td-profit.negative { color: #ef5350; } -.td-kit-cost { font-weight: 500; } -.na { color: #ccc; } +.td-kit-available { font-weight: 500; color: #4a9d7e; background: #f0faf5; } +.td-kit-na { background: #fafafa; } +.na { color: #ddd; } .price-input-wrap { display: inline-flex; align-items: center; gap: 2px; font-size: 13px; color: #3e3a44;