feat: 统一去重检测,所有保存路径禁止同名配方
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Failing after 5s
Test / e2e-test (push) Has been skipped
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Failing after 5s
PR Preview / deploy-preview (pull_request) Has been skipped
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Failing after 5s
Test / e2e-test (push) Has been skipped
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Failing after 5s
PR Preview / deploy-preview (pull_request) Has been skipped
提取 checkDupName 函数统一所有保存路径的重名检测: - 同名同配方:提示已存在,不保存 - 同名不同配方:展示差异,强制改名,循环检测直到不重名 - 覆盖:单条保存、全部保存、共享到公共库 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -981,6 +981,44 @@ function toggleFormTag(tag) {
|
|||||||
else formTags.value.push(tag)
|
else formTags.value.push(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check name against public + personal recipes.
|
||||||
|
* Same name + same content → toast and return false.
|
||||||
|
* Same name + different content → show diff, prompt rename, loop until unique.
|
||||||
|
* Returns final name or false if cancelled.
|
||||||
|
*/
|
||||||
|
async function checkDupName(name, ings) {
|
||||||
|
let currentName = name
|
||||||
|
while (true) {
|
||||||
|
const pubDup = recipeStore.recipes.find(r => r.name === currentName)
|
||||||
|
const diaryDup = diaryStore.userDiary.find(d => d.name === currentName)
|
||||||
|
const dup = pubDup || diaryDup
|
||||||
|
if (!dup) return currentName
|
||||||
|
|
||||||
|
const dupIngs = (dup.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
const myIngs = ings.filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
||||||
|
const identical = dupIngs.length === myIngs.length && dupIngs.every((d, i) => d.oil === myIngs[i].oil && d.drops === myIngs[i].drops)
|
||||||
|
const where = pubDup ? '公共配方库' : '我的配方'
|
||||||
|
|
||||||
|
if (identical) {
|
||||||
|
ui.showToast(`${where}中已有一模一样的配方「${currentName}」`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const existIngs = dupIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
||||||
|
const newIngs = myIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
||||||
|
const ok = await showConfirm(
|
||||||
|
`${where}中已有同名配方「${currentName}」,内容不同:\n\n已有:${existIngs}\n新的:${newIngs}\n\n请改名后保存`,
|
||||||
|
{ okText: '改名', cancelText: '取消' }
|
||||||
|
)
|
||||||
|
if (!ok) return false
|
||||||
|
const newName = await showPrompt('请输入新名称:', currentName)
|
||||||
|
if (!newName || !newName.trim()) return false
|
||||||
|
currentName = newName.trim()
|
||||||
|
// Loop back to check the new name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function saveCurrentRecipe() {
|
async function saveCurrentRecipe() {
|
||||||
if (formVolume.value === 'custom' && !formCustomVolume.value) {
|
if (formVolume.value === 'custom' && !formCustomVolume.value) {
|
||||||
ui.showToast('请输入自定义容量')
|
ui.showToast('请输入自定义容量')
|
||||||
@@ -1010,33 +1048,11 @@ async function saveCurrentRecipe() {
|
|||||||
|
|
||||||
// Dedup check for new recipes (not editing)
|
// Dedup check for new recipes (not editing)
|
||||||
if (!editingRecipe.value) {
|
if (!editingRecipe.value) {
|
||||||
const name = formName.value.trim()
|
const result = await checkDupName(diaryPayload.name, cleanIngs)
|
||||||
// Check public library
|
if (result === false) return // cancelled
|
||||||
const pubDup = recipeStore.recipes.find(r => r.name === name)
|
if (result !== diaryPayload.name) {
|
||||||
// Check personal diary
|
formName.value = result
|
||||||
const diaryDup = diaryStore.userDiary.find(d => d.name === name)
|
diaryPayload.name = result
|
||||||
const dup = pubDup || diaryDup
|
|
||||||
if (dup) {
|
|
||||||
const dupIngs = (dup.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
|
||||||
const myIngs = cleanIngs.filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
|
||||||
const identical = dupIngs.length === myIngs.length && dupIngs.every((ing, i) => ing.oil === myIngs[i].oil && ing.drops === myIngs[i].drops)
|
|
||||||
const where = pubDup ? '公共配方库' : '我的配方'
|
|
||||||
if (identical) {
|
|
||||||
ui.showToast(`${where}中已有一模一样的配方「${name}」`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Show difference
|
|
||||||
const existIngs = dupIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
|
||||||
const newIngs = myIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
|
||||||
const ok = await showConfirm(
|
|
||||||
`${where}中已有同名配方「${name}」,内容不同:\n\n已有:${existIngs}\n新的:${newIngs}\n\n是否改名后保存?`,
|
|
||||||
{ okText: '改名', cancelText: '取消' }
|
|
||||||
)
|
|
||||||
if (!ok) return
|
|
||||||
const newName = await showPrompt('请输入新名称:', name)
|
|
||||||
if (!newName || !newName.trim()) return
|
|
||||||
formName.value = newName.trim()
|
|
||||||
diaryPayload.name = newName.trim()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1129,23 +1145,31 @@ async function saveParsedRecipe(index) {
|
|||||||
async function saveAllParsed() {
|
async function saveAllParsed() {
|
||||||
// Sync current form edits back first
|
// Sync current form edits back first
|
||||||
syncFormToParsed()
|
syncFormToParsed()
|
||||||
let saved = 0
|
const toPublic = auth.canManage && await showConfirm('全部保存到哪里?', { okText: '公共配方库', cancelText: '个人配方' })
|
||||||
for (let i = parsedRecipes.value.length - 1; i >= 0; i--) {
|
let saved = 0, skipped = 0
|
||||||
|
for (let i = 0; i < parsedRecipes.value.length; i++) {
|
||||||
const r = parsedRecipes.value[i]
|
const r = parsedRecipes.value[i]
|
||||||
if (!r.name.trim() || r.ingredients.length === 0) continue
|
if (!r.name.trim() || r.ingredients.length === 0) continue
|
||||||
|
const ings = r.ingredients.map(ing => ({ oil: ing.oil, drops: ing.drops }))
|
||||||
|
const finalName = await checkDupName(r.name.trim(), ings)
|
||||||
|
if (finalName === false) { skipped++; continue }
|
||||||
try {
|
try {
|
||||||
await diaryStore.createDiary({
|
if (toPublic) {
|
||||||
name: r.name.trim(),
|
await recipeStore.saveRecipe({
|
||||||
ingredients: r.ingredients.map(i => ({ oil: i.oil, drops: i.drops })),
|
name: finalName,
|
||||||
note: '',
|
ingredients: ings.map(ing => ({ oil_name: ing.oil, drops: ing.drops })),
|
||||||
tags: [],
|
note: '',
|
||||||
})
|
tags: [],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await diaryStore.createDiary({ name: finalName, ingredients: ings, note: '', tags: [] })
|
||||||
|
}
|
||||||
saved++
|
saved++
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
parsedRecipes.value = []
|
parsedRecipes.value = []
|
||||||
parsedCurrentIndex.value = -1
|
parsedCurrentIndex.value = -1
|
||||||
ui.showToast(`已保存 ${saved} 条配方到我的配方`)
|
ui.showToast(`已保存 ${saved} 条配方到${toPublic ? '公共配方库' : '我的配方'}`)
|
||||||
closeOverlay()
|
closeOverlay()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1424,28 +1448,10 @@ async function recommendApprove(recipe) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function shareDiaryToPublic(diary) {
|
async function shareDiaryToPublic(diary) {
|
||||||
// Check for duplicates in public library
|
const ings = (diary.ingredients || []).map(i => ({ oil: i.oil, drops: i.drops }))
|
||||||
const dup = recipeStore.recipes.find(r => r.name === diary.name)
|
const result = await checkDupName(diary.name, ings)
|
||||||
if (dup) {
|
if (result === false) return
|
||||||
const dIngs = (diary.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
if (result !== diary.name) diary = { ...diary, name: result }
|
||||||
const pIngs = (dup.ingredients || []).filter(i => i.oil).sort((a, b) => a.oil.localeCompare(b.oil))
|
|
||||||
const identical = dIngs.length === pIngs.length && dIngs.every((ing, i) => ing.oil === pIngs[i].oil && ing.drops === pIngs[i].drops)
|
|
||||||
if (identical) {
|
|
||||||
ui.showToast('公共配方库中已有一模一样的配方「' + diary.name + '」')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Same name, different content — show details
|
|
||||||
const existIngs = pIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
|
||||||
const newIngs = dIngs.map(i => `${i.oil}${i.drops}滴`).join('、')
|
|
||||||
const action = await showConfirm(
|
|
||||||
`公共配方库中已有同名配方「${diary.name}」,内容不同:\n\n已有:${existIngs}\n新的:${newIngs}\n\n是否改名后共享?`,
|
|
||||||
{ okText: '改名', cancelText: '取消' }
|
|
||||||
)
|
|
||||||
if (!action) return
|
|
||||||
const newName = await showPrompt('请输入新名称:', diary.name)
|
|
||||||
if (!newName || !newName.trim()) return
|
|
||||||
diary = { ...diary, name: newName.trim() }
|
|
||||||
}
|
|
||||||
|
|
||||||
const ok = await showConfirm(`将「${diary.name}」共享到公共配方库?`)
|
const ok = await showConfirm(`将「${diary.name}」共享到公共配方库?`)
|
||||||
if (!ok) return
|
if (!ok) return
|
||||||
|
|||||||
Reference in New Issue
Block a user