feat: Header重排、共享配方到公共库、待审核配方、权限优化 #17

Merged
fam merged 39 commits from fix/ui-polish-round2 into main 2026-04-09 18:37:12 +00:00
3 changed files with 40 additions and 27 deletions
Showing only changes of commit 765bc0facc - Show all commits

View File

@@ -55,12 +55,12 @@
class="card-brand-bg"
:style="{ backgroundImage: `url('${brand.brand_bg}')` }"
/>
<!-- QR: absolute positioned in export-card, top-right corner of content area -->
<div v-if="brand.qr_code" class="card-qr-wrapper">
<img :src="brand.qr_code" class="card-qr" crossorigin="anonymous" />
<div v-if="brand.brand_name" class="card-qr-name">{{ brand.brand_name }}</div>
</div>
<div class="card-content">
<!-- QR floated right, aligns with brand text top -->
<div v-if="brand.qr_code" class="card-qr-float">
<img :src="brand.qr_code" class="card-qr" crossorigin="anonymous" />
<div v-if="brand.brand_name" class="card-qr-name">{{ brand.brand_name }}</div>
</div>
<div class="card-brand-text">
{{ cardLang === 'en' ? 'doTERRA · Gifts of the Earth' : 'doTERRA · 来自大地的礼物' }}
</div>
@@ -1122,6 +1122,7 @@ async function saveRecipe() {
.card-content {
position: relative;
z-index: 2;
padding-right: 70px; /* leave room for QR */
}
/* Brand overlays */
@@ -1138,14 +1139,15 @@ async function saveRecipe() {
pointer-events: none;
}
.card-qr-float {
float: right;
.card-qr-wrapper {
position: absolute;
top: 36px;
right: 36px;
display: flex;
flex-direction: column;
align-items: center;
gap: 3px;
margin-left: 12px;
margin-bottom: 8px;
z-index: 3;
}
.card-qr {

View File

@@ -170,24 +170,28 @@
<label class="form-label">📋 配方卡片预览</label>
<div class="card-preview-mini">
<div v-if="brandBg" class="card-preview-bg" :style="{ backgroundImage: 'url(' + brandBg + ')' }"></div>
<!-- QR: top-right, aligned with content -->
<img v-if="brandQrImage" :src="brandQrImage" class="card-preview-qr" />
<!-- Content -->
<!-- QR + brand name: top-right -->
<div v-if="brandQrImage" class="card-preview-qr-area">
<img :src="brandQrImage" class="card-preview-qr" />
<div v-if="brandName" class="card-preview-qr-text" :style="{ textAlign: brandAlign }">{{ brandName }}</div>
</div>
<!-- Content: left side -->
<div class="card-preview-content">
<div style="font-size:8px;letter-spacing:2px;color:var(--sage);margin-bottom:4px">doTERRA · 来自大地的礼物</div>
<div style="font-size:14px;font-weight:700;color:var(--text-dark)">配方名称</div>
<div style="font-size:10px;color:var(--text-light);margin-top:2px">薰衣草 · 乳香 · 茶树</div>
</div>
<!-- Total cost bar -->
<div class="card-preview-cost">配方总成本 &nbsp; ¥12.50</div>
<div class="card-preview-cost">
<span>配方总成本</span>
<span>¥12.50</span>
</div>
<!-- Logo left + date right -->
<div class="card-preview-bottom">
<img v-if="brandLogo" :src="brandLogo" class="card-preview-logo" />
<span v-else></span>
<span class="card-preview-date">制作日期{{ new Date().toLocaleDateString('zh-CN') }}</span>
</div>
<!-- Brand name -->
<div v-if="brandName" class="card-preview-brand" :style="{ textAlign: brandAlign }">{{ brandName }}</div>
</div>
</div>
@@ -1064,17 +1068,31 @@ async function applyBusiness() {
.card-preview-content {
position: relative;
z-index: 1;
padding-right: 50px; /* leave room for QR */
padding-right: 60px; /* leave room for QR area */
}
.card-preview-qr {
.card-preview-qr-area {
position: absolute;
top: 16px;
right: 16px;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
z-index: 2;
max-width: 50px;
}
.card-preview-qr {
width: 40px;
height: 40px;
object-fit: contain;
border-radius: 4px;
z-index: 2;
}
.card-preview-qr-text {
font-size: 6px;
color: var(--text-light);
line-height: 1.2;
word-break: break-all;
max-width: 50px;
}
.card-preview-cost {
position: relative;
@@ -1109,14 +1127,7 @@ async function applyBusiness() {
color: var(--text-light);
letter-spacing: 0.5px;
}
.card-preview-brand {
position: relative;
z-index: 1;
margin-top: 4px;
font-size: 8px;
color: var(--text-light);
white-space: pre-line;
}
/* brand name now inside .card-preview-qr-text */
.hint-text {
font-size: 13px;

View File

@@ -174,7 +174,7 @@ onMounted(async () => {
if (openRecipeId) {
router.replace({ path: '/', query: {} })
const tryOpen = () => {
const idx = recipeStore.recipes.findIndex(r => r._id === openRecipeId)
const idx = recipeStore.recipes.findIndex(r => String(r._id) === String(openRecipeId))
if (idx >= 0) {
openDetail(idx)
return true