feat: 精油知识卡片加品牌元素 + 导出PDF修复
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 6s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 6s
PR Preview / deploy-preview (pull_request) Successful in 8s
Test / e2e-test (push) Has been cancelled
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 6s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 6s
PR Preview / deploy-preview (pull_request) Successful in 8s
Test / e2e-test (push) Has been cancelled
- 知识卡片: QR码(右上角)+品牌名+背景图+Logo - 导出PDF: volumeLabel传name参数修复,单价列适配单位 - 导入api模块修复品牌数据加载 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,9 +181,16 @@
|
||||
|
||||
<!-- Oil Knowledge Card Modal -->
|
||||
<div v-if="activeCard && activeCardName" class="modal-overlay" @mousedown.self="closeOilModal">
|
||||
<div class="oil-card-modal">
|
||||
<div class="oil-card-header">
|
||||
<div class="oil-card-header-content">
|
||||
<div class="oil-card-modal" style="position:relative;overflow:hidden">
|
||||
<!-- Brand background -->
|
||||
<div v-if="brand.brand_bg" style="position:absolute;inset:0;width:100%;height:100%;background-size:cover;background-position:center;opacity:0.08;pointer-events:none;z-index:0" :style="{ backgroundImage: `url('${brand.brand_bg}')` }"></div>
|
||||
<!-- QR code -->
|
||||
<div v-if="brand.qr_code" style="position:absolute;top:16px;right:16px;display:flex;flex-direction:column;gap:3px;z-index:3" :style="{ alignItems: (brand.brand_align === 'left' ? 'flex-start' : brand.brand_align === 'right' ? 'flex-end' : 'center') }">
|
||||
<img :src="brand.qr_code" crossorigin="anonymous" style="width:48px;height:48px;object-fit:cover;border-radius:6px;box-shadow:0 2px 6px rgba(0,0,0,0.1)" />
|
||||
<div v-if="brand.brand_name" style="font-size:7px;color:rgba(255,255,255,0.8);line-height:1.3;max-width:60px;white-space:pre-line;text-align:center">{{ brand.brand_name }}</div>
|
||||
</div>
|
||||
<div class="oil-card-header" style="position:relative;z-index:1">
|
||||
<div class="oil-card-header-content" :style="brand.qr_code ? 'padding-right:70px' : ''">
|
||||
<span class="oil-card-emoji">{{ activeCard.emoji }}</span>
|
||||
<div>
|
||||
<h2 class="oil-card-title">{{ activeCardName }}</h2>
|
||||
@@ -196,7 +203,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-close btn-close-light" @click="closeOilModal">✕</button>
|
||||
<button class="btn-close btn-close-light" @click="closeOilModal" style="z-index:4">✕</button>
|
||||
</div>
|
||||
<!-- Method badges -->
|
||||
<div class="oil-card-methods">
|
||||
@@ -227,6 +234,10 @@
|
||||
<h4 class="oil-card-caution-title">⚠️ 注意事项</h4>
|
||||
<p>{{ activeCard.caution }}</p>
|
||||
</div>
|
||||
<!-- Logo -->
|
||||
<div v-if="brand.brand_logo" style="padding-top:8px">
|
||||
<img :src="brand.brand_logo" crossorigin="anonymous" style="height:24px;opacity:0.7" />
|
||||
</div>
|
||||
<div style="text-align:center;padding-top:12px">
|
||||
<button class="btn btn-outline btn-sm" @click="saveCardImage(activeCardName)">💾 保存图片</button>
|
||||
</div>
|
||||
@@ -408,12 +419,19 @@ import { useRecipesStore } from '../stores/recipes'
|
||||
import { oilEn } from '../composables/useOilTranslation'
|
||||
import { getOilCard, setOilCard } from '../composables/useOilCards'
|
||||
import { showConfirm } from '../composables/useDialog'
|
||||
import { api } from '../composables/useApi'
|
||||
|
||||
const auth = useAuthStore()
|
||||
const oils = useOilsStore()
|
||||
const recipeStore = useRecipesStore()
|
||||
const ui = useUiStore()
|
||||
|
||||
// Brand data for card
|
||||
const brand = ref({})
|
||||
async function loadBrand() {
|
||||
try { brand.value = await api.get('/api/brand') } catch {}
|
||||
}
|
||||
|
||||
// Modal states
|
||||
const showDilution = ref(false)
|
||||
const showContra = ref(false)
|
||||
@@ -637,6 +655,7 @@ async function openOilDetail(name) {
|
||||
activeCardName.value = name
|
||||
activeCard.value = card
|
||||
selectedOilName.value = null
|
||||
loadBrand()
|
||||
// Pre-generate card image for instant save
|
||||
oilCardImageUrl.value = null
|
||||
await nextTick()
|
||||
@@ -825,8 +844,9 @@ function exportPDF() {
|
||||
const en = getEnglishName(name)
|
||||
const bp = meta.bottlePrice != null ? '¥' + meta.bottlePrice.toFixed(0) : '--'
|
||||
const rp = meta.retailPrice != null ? '¥' + meta.retailPrice.toFixed(0) : '--'
|
||||
const vol = volumeLabel(meta.dropCount)
|
||||
const ppd = oils.pricePerDrop(name) ? '¥' + oils.pricePerDrop(name).toFixed(2) : '--'
|
||||
const vol = volumeLabel(meta.dropCount, name)
|
||||
const unit = oilPriceUnit(name)
|
||||
const ppd = oils.pricePerDrop(name) ? '¥' + oils.pricePerDrop(name).toFixed(2) + '/' + unit : '--'
|
||||
rows += `<tr>
|
||||
<td>${name}</td>
|
||||
<td>${en}</td>
|
||||
@@ -858,7 +878,7 @@ function exportPDF() {
|
||||
<h1>doTERRA 精油价目表 ${dateStr}</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>精油</th><th>英文名</th><th>会员价</th><th>零售价</th><th>容量</th><th>单价/滴</th></tr>
|
||||
<tr><th>精油</th><th>英文名</th><th>会员价</th><th>零售价</th><th>容量</th><th>单价</th></tr>
|
||||
</thead>
|
||||
<tbody>${rows}</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user