feat: 配方卡片容量切换、预览/保存流程优化、精油搜索自动补全、精油英文名编辑
Some checks failed
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 4s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 13s
Test / e2e-test (push) Failing after 1m20s

1. 配方卡片视图加回容量切换按钮(单次/2.5ml/5ml…),
   非单次容量的滴数通过 Math.round 取整显示
2. 编辑器「预览」按钮改为展示当前未保存数据;
   预览后点关闭询问是否保存;
   直接点「保存」后留在配方卡片视图(不再关闭弹层)
3. 添加精油改为搜索输入框 + 下拉自动补全,
   支持中文名和英文名筛选
4. 精油价目:添加/编辑表单加入英文名字段;
   编辑/删除按钮改为悬停(桌面)或点击(移动端)才显示;
   后端及数据库同步支持 oils.en_name
This commit is contained in:
2026-04-08 20:03:04 +00:00
parent cc79ae1211
commit 31b46d59b6
5 changed files with 230 additions and 28 deletions

View File

@@ -75,6 +75,7 @@
<h3 class="section-title">添加精油</h3>
<div class="form-row">
<input v-model="newOilName" class="form-input" placeholder="精油名称" />
<input v-model="newOilEnName" class="form-input" placeholder="英文名 (English)" />
<input v-model.number="newBottlePrice" class="form-input-sm" type="number" placeholder="瓶价 ¥" />
<select v-model="newVolume" class="form-select">
<option value="2.5">2.5ml (46)</option>
@@ -129,7 +130,7 @@
v-for="name in filteredOilNames"
:key="name"
class="oil-chip"
:class="{ 'oil-chip--inactive': getMeta(name)?.isActive === false }"
:class="{ 'oil-chip--inactive': getMeta(name)?.isActive === false, 'oil-chip--active': activeChip === name }"
@click="openOilDetail(name)"
>
<span v-if="getOilCard(name)" class="oil-badge" title="有知识卡片">📖</span>
@@ -159,7 +160,7 @@
<div class="oil-chip-volume" v-if="getMeta(name)?.dropCount">
{{ volumeLabel(getMeta(name).dropCount) }}
</div>
<div class="oil-actions" v-if="auth.canManage" @click.stop>
<div class="oil-actions" v-if="auth.canManage" @click.stop @touchstart.stop="toggleChip(name)">
<button class="btn-icon-sm" @click="editOil(name)" title="编辑"></button>
<button class="btn-icon-sm" @click="removeOil(name)" title="删除">🗑</button>
</div>
@@ -271,6 +272,10 @@
<button class="btn-close" @click="editingOilName = null"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label>英文名</label>
<input v-model="editOilEnName" class="form-input" type="text" placeholder="English name" />
</div>
<div class="form-group">
<label>瓶价 (¥)</label>
<input v-model.number="editBottlePrice" class="form-input" type="number" />
@@ -334,6 +339,7 @@ const activeCard = ref(null)
// Add oil form
const newOilName = ref('')
const newOilEnName = ref('')
const newBottlePrice = ref(null)
const newVolume = ref('5')
const newCustomDrops = ref(null)
@@ -345,6 +351,14 @@ const editBottlePrice = ref(0)
const editVolume = ref('5')
const editDropCount = ref(0)
const editRetailPrice = ref(null)
const editOilEnName = ref('')
// Active chip (for mobile hover)
const activeChip = ref(null)
function toggleChip(name) {
activeChip.value = activeChip.value === name ? null : name
}
// Volume-to-drops mapping
const VOLUME_OPTIONS = {
@@ -407,9 +421,13 @@ function getMeta(name) {
}
function getEnglishName(name) {
// First check the oil card for English name
// 1. Oil card has priority
const card = getOilCard(name)
if (card && card.en) return card.en
// 2. Stored en_name in meta
const meta = oils.oilsMeta[name]
if (meta?.enName) return meta.enName
// 3. Static translation map
return oilEn(name)
}
@@ -466,10 +484,12 @@ async function addOil() {
newOilName.value.trim(),
newBottlePrice.value || 0,
dropCount,
newRetailPrice.value || null
newRetailPrice.value || null,
newOilEnName.value.trim() || null
)
ui.showToast(`已添加: ${newOilName.value}`)
newOilName.value = ''
newOilEnName.value = ''
newBottlePrice.value = null
newVolume.value = '5'
newCustomDrops.value = null
@@ -487,6 +507,7 @@ function editOil(name) {
editVolume.value = dropCountToVolume(dc)
editDropCount.value = dc
editRetailPrice.value = meta?.retailPrice || null
editOilEnName.value = meta?.enName || getEnglishName(name) || ''
}
async function saveEditOil() {
@@ -496,7 +517,8 @@ async function saveEditOil() {
editingOilName.value,
editBottlePrice.value,
dropCount,
editRetailPrice.value
editRetailPrice.value,
editOilEnName.value.trim() || null
)
ui.showToast('已更新')
editingOilName.value = null
@@ -982,7 +1004,8 @@ function saveContraImage() {
transition: opacity 0.15s;
}
.oil-chip:hover .oil-actions {
.oil-chip:hover .oil-actions,
.oil-chip--active .oil-actions {
opacity: 1;
}