rewrite: 英文名统一读写 oils.en_name,三处同步
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 3s
PR Preview / test (pull_request) Successful in 4s
PR Preview / deploy-preview (pull_request) Successful in 14s
Test / e2e-test (push) Failing after 1m23s

核心设计: oils.en_name (DB) 是唯一数据源

读取链路 (全部统一):
- 精油价目: getEnglishName → oilsMeta.enName → oilEn()
- 配方卡片: getCardOilName → oilsMeta.enName → oilEn()
- 翻译编辑器: openTranslationEditor → oilsMeta.enName

写入链路 (全部写同一个字段):
- 精油价目编辑: saveEditOil → oilsStore.saveOil → oils.en_name → loadOils
- 配方卡翻译: applyTranslation → oilsStore.saveOil → oils.en_name → loadOils
- 配方名翻译: applyTranslation → PUT /api/recipes/{id} → recipes.en_name → loadRecipes

保存后:
- await Promise.all([loadOils(), loadRecipes()]) 刷新所有数据
- 下次任何地方渲染都读到最新值
- customOilNameEn 只在编辑器打开期间有效,关闭后丢弃

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 17:04:00 +00:00
parent 9dbaf95839
commit b69abe0fdb

View File

@@ -596,41 +596,41 @@ function copyText() {
} }
function openTranslationEditor() { function openTranslationEditor() {
// Pre-populate with existing English names from DB/translation table // Pre-populate from single source of truth: oilsMeta.enName (DB)
const map = {} const map = {}
for (const ing of cardIngredients.value) { for (const ing of cardIngredients.value) {
const existing = oilsStore.oilsMeta[ing.oil]?.enName || oilEn(ing.oil) map[ing.oil] = getOilEnglish(ing.oil)
if (existing) map[ing.oil] = existing
} }
customOilNameEn.value = map customOilNameEn.value = map
customRecipeNameEn.value = recipe.value.en_name || recipeNameEn(recipe.value.name) customRecipeNameEn.value = recipe.value.en_name || ''
showTranslationEditor.value = true showTranslationEditor.value = true
} }
async function applyTranslation() { async function applyTranslation() {
showTranslationEditor.value = false showTranslationEditor.value = false
let saved = 0 let saved = 0
let failed = 0
// 1. Save recipe English name (no version check — only updating en_name) // 1. Save recipe English name to recipes table
if (recipe.value._id && customRecipeNameEn.value) { if (recipe.value._id && customRecipeNameEn.value.trim()) {
try { try {
await api.put(`/api/recipes/${recipe.value._id}`, { await api.put(`/api/recipes/${recipe.value._id}`, {
en_name: customRecipeNameEn.value, en_name: customRecipeNameEn.value.trim(),
}) })
recipe.value.en_name = customRecipeNameEn.value
saved++ saved++
} catch (e) { } catch (e) {
console.error('Save recipe en_name failed:', e) console.error('Save recipe en_name failed:', e)
failed++
} }
} }
// 2. Save each oil's English name to oils table (syncs with oil reference page) // 2. Save each oil's English name to oils table
let failed = 0 // This is THE single source of truth — both oil reference page and recipe card read from here
for (const [oilName, enName] of Object.entries(customOilNameEn.value)) { for (const [oilName, enName] of Object.entries(customOilNameEn.value)) {
if (!enName?.trim()) continue if (!enName?.trim()) continue
const meta = oilsStore.oilsMeta[oilName] const meta = oilsStore.oilsMeta[oilName]
if (!meta) continue if (!meta) continue
if (meta.enName === enName.trim()) continue if (meta.enName === enName.trim()) continue // no change
try { try {
await oilsStore.saveOil(oilName, meta.bottlePrice, meta.dropCount, meta.retailPrice, enName.trim()) await oilsStore.saveOil(oilName, meta.bottlePrice, meta.dropCount, meta.retailPrice, enName.trim())
saved++ saved++
@@ -640,25 +640,38 @@ async function applyTranslation() {
} }
} }
// 3. Reload ALL data — this updates oilsMeta.enName and recipe.en_name
// So the next render reads fresh data from the single source
await Promise.all([
oilsStore.loadOils(),
recipesStore.loadRecipes(),
])
if (saved > 0) { if (saved > 0) {
ui.showToast(`翻译已保存(${saved}项)` + (failed > 0 ? `${failed}项失败` : '')) ui.showToast(`翻译已保存(${saved}项)` + (failed > 0 ? `${failed}项失败` : ''))
// Reload data so next open shows updated names
recipesStore.loadRecipes()
oilsStore.loadOils()
} else if (failed > 0) { } else if (failed > 0) {
ui.showToast(`保存失败 ${failed}`) ui.showToast(`保存失败 ${failed}`)
} else { } else {
ui.showToast('没有修改') ui.showToast('没有修改')
} }
// Regenerate card image with updated names from store
cardImageUrl.value = null cardImageUrl.value = null
nextTick(() => generateCardImage()) nextTick(() => generateCardImage())
} }
// Override translation getters for card rendering // Override translation getters for card rendering
function getOilEnglish(name) {
return oilsStore.oilsMeta[name]?.enName || oilEn(name) || ''
}
function getCardOilName(name) { function getCardOilName(name) {
if (cardLang.value === 'en') { if (cardLang.value === 'en') {
return customOilNameEn.value[name] || oilsStore.oilsMeta[name]?.enName || oilEn(name) || name // During editing, use customOilNameEn; otherwise read from store (single source of truth)
if (showTranslationEditor.value && customOilNameEn.value[name]) {
return customOilNameEn.value[name]
}
return getOilEnglish(name) || name
} }
return name return name
} }