feat: 权限细化、商业认证跳转、UI改进 #19
@@ -62,6 +62,7 @@
|
||||
<div class="recipe-section">
|
||||
<h3 class="section-title">
|
||||
<span>📖 我的配方 ({{ myRecipes.length }})</span>
|
||||
<span v-if="!auth.isAdmin && sharedCount > 0" class="contrib-tag">已贡献 {{ sharedCount }} 条</span>
|
||||
<button class="btn-sm btn-outline" @click="toggleSelectAllDiary">全选/取消</button>
|
||||
</h3>
|
||||
<div class="recipe-list">
|
||||
@@ -84,8 +85,11 @@
|
||||
<span v-for="t in (d.tags || [])" :key="t" class="mini-tag">{{ t }}</span>
|
||||
</span>
|
||||
<span class="row-cost">{{ oils.fmtPrice(oils.calcCost(d.ingredients || [])) }}</span>
|
||||
<span v-if="getDiaryShareStatus(d) === 'shared'" class="share-tag shared">已共享</span>
|
||||
<span v-else-if="getDiaryShareStatus(d) === 'pending'" class="share-tag pending">等待审核</span>
|
||||
</div>
|
||||
<div class="row-actions">
|
||||
<button v-if="getDiaryShareStatus(d) !== 'shared'" class="btn-icon" @click="shareDiaryToPublic(d)" title="共享到公共配方库">📤</button>
|
||||
<button class="btn-icon" @click="editDiaryRecipe(d)" title="编辑">✏️</button>
|
||||
<button class="btn-icon" @click="removeDiaryRecipe(d)" title="删除">🗑️</button>
|
||||
</div>
|
||||
@@ -609,10 +613,19 @@ async function saveAllParsed() {
|
||||
closeOverlay()
|
||||
}
|
||||
|
||||
const sharedCount = ref(0)
|
||||
|
||||
// Load diary on mount
|
||||
onMounted(async () => {
|
||||
if (auth.isLoggedIn) {
|
||||
await diaryStore.loadDiary()
|
||||
try {
|
||||
const res = await api('/api/me/contribution')
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
sharedCount.value = data.shared_count || 0
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -625,6 +638,40 @@ function editDiaryRecipe(diary) {
|
||||
showAddOverlay.value = true
|
||||
}
|
||||
|
||||
function getDiaryShareStatus(d) {
|
||||
// Check if a public recipe with same name exists, owned by current user or adopted by admin
|
||||
const pub = recipeStore.recipes.find(r => r.name === d.name)
|
||||
if (!pub) return null
|
||||
if (pub._owner_id === auth.user?.id) return 'pending'
|
||||
return 'shared'
|
||||
}
|
||||
|
||||
async function shareDiaryToPublic(diary) {
|
||||
const ok = await showConfirm(`将「${diary.name}」共享到公共配方库?`)
|
||||
if (!ok) return
|
||||
try {
|
||||
const res = await api('/api/recipes', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
name: diary.name,
|
||||
note: diary.note || '',
|
||||
ingredients: (diary.ingredients || []).map(i => ({ oil_name: i.oil, drops: i.drops })),
|
||||
tags: diary.tags || [],
|
||||
}),
|
||||
})
|
||||
if (res.ok) {
|
||||
if (auth.isAdmin) {
|
||||
ui.showToast('已共享到公共配方库')
|
||||
} else {
|
||||
ui.showToast('已提交,等待管理员审核')
|
||||
}
|
||||
await recipeStore.loadRecipes()
|
||||
}
|
||||
} catch {
|
||||
ui.showToast('共享失败')
|
||||
}
|
||||
}
|
||||
|
||||
async function removeDiaryRecipe(diary) {
|
||||
const ok = await showConfirm(`确定删除个人配方 "${diary.name}"?`)
|
||||
if (!ok) return
|
||||
@@ -928,6 +975,25 @@ watch(() => recipeStore.recipes, () => {
|
||||
color: #6b6375;
|
||||
}
|
||||
|
||||
.share-tag {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.share-tag.shared { background: #e8f5e9; color: #2e7d32; }
|
||||
.share-tag.pending { background: #fff3e0; color: #e65100; }
|
||||
|
||||
.contrib-tag {
|
||||
font-size: 11px;
|
||||
color: #4a9d7e;
|
||||
background: #e8f5e9;
|
||||
padding: 2px 8px;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.row-cost {
|
||||
font-size: 13px;
|
||||
color: #4a9d7e;
|
||||
|
||||
@@ -64,7 +64,6 @@
|
||||
/>
|
||||
<span v-if="getDiaryShareStatus(d) === 'shared'" class="share-status shared">已共享</span>
|
||||
<span v-else-if="getDiaryShareStatus(d) === 'pending'" class="share-status pending">审核中</span>
|
||||
<button v-else class="share-btn" @click.stop="shareDiaryToPublic(d)" title="共享到公共配方库">📤</button>
|
||||
</div>
|
||||
<div v-if="myDiaryRecipes.length === 0" class="empty-hint">暂无个人配方</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user