feat: 新功能改进 #20

Merged
hera merged 57 commits from feat/next-improvements into main 2026-04-10 20:30:37 +00:00
2 changed files with 55 additions and 4 deletions
Showing only changes of commit 9f0c66e583 - Show all commits

View File

@@ -1238,14 +1238,15 @@ def create_diary(body: dict, user=Depends(get_current_user)):
name = body.get("name", "").strip()
ingredients = body.get("ingredients", [])
note = body.get("note", "")
tags = body.get("tags", [])
source_id = body.get("source_recipe_id")
if not name:
raise HTTPException(400, "请输入配方名称")
conn = get_db()
c = conn.cursor()
c.execute(
"INSERT INTO user_diary (user_id, source_recipe_id, name, ingredients, note) VALUES (?, ?, ?, ?, ?)",
(user["id"], source_id, name, json.dumps(ingredients, ensure_ascii=False), note)
"INSERT INTO user_diary (user_id, source_recipe_id, name, ingredients, note, tags) VALUES (?, ?, ?, ?, ?, ?)",
(user["id"], source_id, name, json.dumps(ingredients, ensure_ascii=False), note, json.dumps(tags, ensure_ascii=False))
)
conn.commit()
did = c.lastrowid

View File

@@ -54,7 +54,11 @@
class="tag-chip"
:class="{ active: selectedTags.includes(tag) }"
@click="toggleTag(tag)"
>{{ tag }}</span>
>{{ tag }}<span v-if="auth.isAdmin" class="tag-delete" @click.stop="deleteGlobalTag(tag)">×</span></span>
<div v-if="auth.canEdit" class="tag-add-row">
<input v-model="globalNewTag" class="tag-add-input" placeholder="新标签..." @keydown.enter="addGlobalTag" />
<button class="tag-add-btn" @click="addGlobalTag" :disabled="!globalNewTag.trim()">+</button>
</div>
</div>
</div>
@@ -486,6 +490,38 @@ function filterBySearchAndTags(list) {
const myFilteredRecipes = computed(() => filterBySearchAndTags(myRecipes.value))
const publicFilteredRecipes = computed(() => filterBySearchAndTags(publicRecipes.value))
const globalNewTag = ref('')
async function addGlobalTag() {
const tag = globalNewTag.value.trim()
if (!tag) return
try {
await api('/api/tags', { method: 'POST', body: JSON.stringify({ name: tag }) })
if (!recipeStore.allTags.includes(tag)) {
recipeStore.allTags.push(tag)
recipeStore.allTags.sort((a, b) => a.localeCompare(b, 'zh'))
}
globalNewTag.value = ''
ui.showToast('标签已添加')
} catch {
ui.showToast('添加失败')
}
}
async function deleteGlobalTag(tag) {
const { showConfirm } = await import('../composables/useDialog')
const ok = await showConfirm(`确定删除标签「${tag}」?`)
if (!ok) return
try {
await api(`/api/tags/${encodeURIComponent(tag)}`, { method: 'DELETE' })
const idx = recipeStore.allTags.indexOf(tag)
if (idx >= 0) recipeStore.allTags.splice(idx, 1)
ui.showToast('标签已删除')
} catch {
ui.showToast('删除失败')
}
}
function toggleTag(tag) {
const idx = selectedTags.value.indexOf(tag)
if (idx >= 0) selectedTags.value.splice(idx, 1)
@@ -712,9 +748,15 @@ const formCandidateTags = computed(() =>
function addNewFormTag() {
const tag = newTagInput.value.trim()
if (tag && !formTags.value.includes(tag)) {
if (!tag) return
if (!formTags.value.includes(tag)) {
formTags.value.push(tag)
}
// Also add to global tags so it appears in candidates if removed
if (!recipeStore.allTags.includes(tag)) {
recipeStore.allTags.push(tag)
recipeStore.allTags.sort((a, b) => a.localeCompare(b, 'zh'))
}
newTagInput.value = ''
}
@@ -1546,6 +1588,14 @@ watch(() => recipeStore.recipes, () => {
.drops-sm:focus { border-color: #7ec6a4; }
.select-sm { padding: 4px 6px; border: 1.5px solid #d4cfc7; border-radius: 6px; font-size: 12px; font-family: inherit; background: #fff; width: auto; }
.btn-select-active { background: #e8f5e9; color: #2e7d5a; border: 1.5px solid #7ec6a4; border-radius: 10px; padding: 7px 14px; font-size: 13px; cursor: pointer; font-family: inherit; font-weight: 600; }
.tag-delete { margin-left: 4px; cursor: pointer; font-size: 12px; color: #ccc; }
.tag-delete:hover { color: #c0392b; }
.tag-add-row { display: flex; gap: 4px; align-items: center; width: 100%; margin-top: 4px; }
.tag-add-input { flex: 1; padding: 4px 8px; border: 1px solid #e5e4e7; border-radius: 8px; font-size: 12px; outline: none; font-family: inherit; }
.tag-add-input:focus { border-color: #7ec6a4; }
.tag-add-btn { border: none; background: #e8f5e9; color: #4a9d7e; border-radius: 6px; padding: 4px 10px; font-size: 12px; cursor: pointer; font-family: inherit; }
.tag-add-btn:disabled { opacity: 0.4; }
.export-btn {
margin-left: auto;
border: none; background: none; cursor: pointer; font-size: 16px; padding: 4px 6px;