2058 lines
126 KiB
HTML
2058 lines
126 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>🌿 精油配方计算器</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;400;600;700&family=Noto+Sans+SC:wght@300;400;500&display=swap" rel="stylesheet">
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/exceljs@4.4.0/dist/exceljs.min.js"></script>
|
||
<style>
|
||
:root {
|
||
--cream: #faf6f0;
|
||
--warm-white: #fffdf9;
|
||
--sage: #7a9e7e;
|
||
--sage-dark: #5a7d5e;
|
||
--sage-light: #c8ddc9;
|
||
--sage-mist: #eef4ee;
|
||
--gold: #c9a84c;
|
||
--gold-light: #f0e4c0;
|
||
--brown: #6b4f3a;
|
||
--brown-light: #c4a882;
|
||
--text-dark: #2c2416;
|
||
--text-mid: #5a4a35;
|
||
--text-light: #9a8570;
|
||
--border: #e0d4c0;
|
||
--shadow: 0 4px 20px rgba(90,60,30,0.08);
|
||
--shadow-hover: 0 8px 32px rgba(90,60,30,0.15);
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
body {
|
||
font-family: 'Noto Sans SC', sans-serif;
|
||
background: var(--cream);
|
||
color: var(--text-dark);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* Header */
|
||
.app-header {
|
||
background: linear-gradient(135deg, #3d6b41 0%, #5a7d5e 50%, #7a9e7e 100%);
|
||
padding: 28px 32px 24px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.app-header::before {
|
||
content: '';
|
||
position: absolute; inset: 0;
|
||
background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.05'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
|
||
}
|
||
.header-inner { position: relative; z-index: 1; display: flex; align-items: center; gap: 16px; }
|
||
.header-icon { font-size: 40px; }
|
||
.header-title { color: white; }
|
||
.header-title h1 { font-family: 'Noto Serif SC', serif; font-size: 24px; font-weight: 600; letter-spacing: 2px; }
|
||
.header-title p { font-size: 13px; opacity: 0.8; margin-top: 4px; letter-spacing: 1px; }
|
||
|
||
/* Nav tabs */
|
||
.nav-tabs {
|
||
display: flex;
|
||
background: white;
|
||
border-bottom: 1px solid var(--border);
|
||
padding: 0 24px;
|
||
gap: 0;
|
||
overflow-x: auto;
|
||
}
|
||
.nav-tab {
|
||
padding: 14px 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: var(--text-light);
|
||
cursor: pointer;
|
||
border-bottom: 3px solid transparent;
|
||
white-space: nowrap;
|
||
transition: all 0.2s;
|
||
}
|
||
.nav-tab:hover { color: var(--sage-dark); }
|
||
.nav-tab.active { color: var(--sage-dark); border-bottom-color: var(--sage); }
|
||
|
||
/* Main content */
|
||
.main { padding: 24px; max-width: 960px; margin: 0 auto; }
|
||
|
||
/* Section */
|
||
.section { display: none; }
|
||
.section.active { display: block; }
|
||
|
||
/* Search box */
|
||
.search-box {
|
||
background: white;
|
||
border-radius: 16px;
|
||
padding: 20px 24px;
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: 20px;
|
||
}
|
||
.search-label { font-size: 13px; color: var(--text-light); margin-bottom: 10px; letter-spacing: 0.5px; }
|
||
.search-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
|
||
.search-input {
|
||
flex: 1; min-width: 200px;
|
||
padding: 11px 16px;
|
||
border: 1.5px solid var(--border);
|
||
border-radius: 10px;
|
||
font-size: 15px;
|
||
font-family: inherit;
|
||
color: var(--text-dark);
|
||
background: var(--cream);
|
||
transition: border-color 0.2s;
|
||
outline: none;
|
||
}
|
||
.search-input:focus { border-color: var(--sage); background: white; }
|
||
.btn {
|
||
padding: 11px 22px;
|
||
border-radius: 10px;
|
||
border: none;
|
||
font-size: 14px;
|
||
font-family: inherit;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
white-space: nowrap;
|
||
}
|
||
.btn-primary { background: var(--sage); color: white; }
|
||
.btn-primary:hover { background: var(--sage-dark); transform: translateY(-1px); box-shadow: 0 4px 12px rgba(90,125,94,0.3); }
|
||
.btn-gold { background: var(--gold); color: white; }
|
||
.btn-gold:hover { background: #b8973e; transform: translateY(-1px); }
|
||
.btn-outline { background: transparent; color: var(--sage-dark); border: 1.5px solid var(--sage); }
|
||
.btn-outline:hover { background: var(--sage-mist); }
|
||
.btn-danger { background: transparent; color: #c0392b; border: 1.5px solid #e8b4b0; }
|
||
.btn-danger:hover { background: #fdf0ee; }
|
||
.btn-sm { padding: 7px 14px; font-size: 13px; }
|
||
|
||
/* Recipe grid */
|
||
.recipe-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 24px;
|
||
}
|
||
.recipe-card {
|
||
background: white;
|
||
border-radius: 14px;
|
||
padding: 18px;
|
||
cursor: pointer;
|
||
box-shadow: var(--shadow);
|
||
border: 2px solid transparent;
|
||
transition: all 0.2s;
|
||
position: relative;
|
||
}
|
||
.recipe-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); border-color: var(--sage-light); }
|
||
.recipe-card.selected { border-color: var(--sage); background: var(--sage-mist); }
|
||
.recipe-card-name {
|
||
font-family: 'Noto Serif SC', serif;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--text-dark);
|
||
margin-bottom: 8px;
|
||
}
|
||
.recipe-card-oils { font-size: 12px; color: var(--text-light); line-height: 1.7; }
|
||
.recipe-card-price {
|
||
margin-top: 12px;
|
||
font-size: 13px;
|
||
color: var(--sage-dark);
|
||
font-weight: 600;
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
|
||
/* Detail panel */
|
||
.detail-panel {
|
||
background: white;
|
||
border-radius: 16px;
|
||
padding: 28px;
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: 24px;
|
||
}
|
||
.detail-header {
|
||
display: flex; justify-content: space-between; align-items: flex-start;
|
||
margin-bottom: 24px; flex-wrap: wrap; gap: 12px;
|
||
}
|
||
.detail-title {
|
||
font-family: 'Noto Serif SC', serif;
|
||
font-size: 22px; font-weight: 700; color: var(--text-dark);
|
||
}
|
||
.detail-note {
|
||
font-size: 13px; color: var(--text-light);
|
||
background: var(--gold-light); border-radius: 8px;
|
||
padding: 6px 12px; margin-top: 6px;
|
||
display: inline-block;
|
||
}
|
||
.detail-actions { display: flex; gap: 10px; flex-wrap: wrap; }
|
||
|
||
/* Ingredients table */
|
||
.ingredients-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
|
||
.ingredients-table th {
|
||
text-align: left; padding: 10px 14px;
|
||
font-size: 12px; font-weight: 600;
|
||
color: var(--text-light); letter-spacing: 0.5px;
|
||
border-bottom: 2px solid var(--border);
|
||
text-transform: uppercase;
|
||
}
|
||
.ingredients-table td {
|
||
padding: 12px 14px;
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: 14px; vertical-align: middle;
|
||
}
|
||
.ingredients-table tr:last-child td { border-bottom: none; }
|
||
.ingredients-table tr:hover td { background: var(--sage-mist); }
|
||
|
||
.drops-input {
|
||
width: 70px; padding: 6px 10px;
|
||
border: 1.5px solid var(--border); border-radius: 8px;
|
||
font-size: 14px; font-family: inherit; text-align: center;
|
||
outline: none; transition: border-color 0.2s;
|
||
}
|
||
.drops-input:focus { border-color: var(--sage); }
|
||
|
||
.oil-select {
|
||
padding: 6px 10px;
|
||
border: 1.5px solid var(--border); border-radius: 8px;
|
||
font-size: 13px; font-family: inherit;
|
||
background: white; outline: none;
|
||
max-width: 160px;
|
||
}
|
||
.oil-select:focus { border-color: var(--sage); }
|
||
|
||
.remove-btn {
|
||
background: none; border: none; cursor: pointer;
|
||
color: #c0392b; font-size: 18px; padding: 4px 8px;
|
||
border-radius: 6px; transition: background 0.2s;
|
||
}
|
||
.remove-btn:hover { background: #fdf0ee; }
|
||
|
||
.total-row {
|
||
background: var(--sage-mist);
|
||
border-radius: 12px; padding: 16px 20px;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
margin-top: 16px;
|
||
}
|
||
.total-label { font-size: 14px; color: var(--text-mid); font-weight: 500; }
|
||
.total-price { font-size: 22px; font-weight: 700; color: var(--sage-dark); }
|
||
|
||
/* Add ingredient */
|
||
.add-ingredient-row {
|
||
display: flex; gap: 10px; align-items: center;
|
||
margin-top: 12px; flex-wrap: wrap;
|
||
}
|
||
.add-ingredient-row select, .add-ingredient-row input {
|
||
padding: 8px 12px; border: 1.5px solid var(--border);
|
||
border-radius: 8px; font-size: 13px; font-family: inherit;
|
||
outline: none;
|
||
}
|
||
.add-ingredient-row select:focus, .add-ingredient-row input:focus { border-color: var(--sage); }
|
||
|
||
/* Card preview for export */
|
||
.card-preview-wrapper { margin-top: 20px; }
|
||
#recipe-card-export {
|
||
background: linear-gradient(145deg, #faf7f0 0%, #f5ede0 100%);
|
||
border-radius: 20px;
|
||
padding: 36px;
|
||
font-family: 'Noto Serif SC', serif;
|
||
max-width: 480px;
|
||
border: 1px solid #e0ccaa;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
#recipe-card-export::before {
|
||
content: '';
|
||
position: absolute; top: -40px; right: -40px;
|
||
width: 180px; height: 180px;
|
||
background: radial-gradient(circle, rgba(122,158,126,0.15) 0%, transparent 70%);
|
||
border-radius: 50%;
|
||
}
|
||
#recipe-card-export::after {
|
||
content: '';
|
||
position: absolute; bottom: -30px; left: -30px;
|
||
width: 140px; height: 140px;
|
||
background: radial-gradient(circle, rgba(201,168,76,0.12) 0%, transparent 70%);
|
||
border-radius: 50%;
|
||
}
|
||
.card-brand {
|
||
font-size: 11px; letter-spacing: 3px; color: var(--sage);
|
||
text-transform: uppercase; margin-bottom: 8px;
|
||
}
|
||
.card-title {
|
||
font-size: 26px; font-weight: 700; color: var(--text-dark);
|
||
margin-bottom: 6px; line-height: 1.3;
|
||
}
|
||
.card-divider {
|
||
width: 48px; height: 2px;
|
||
background: linear-gradient(90deg, var(--sage), var(--gold));
|
||
border-radius: 2px; margin: 14px 0;
|
||
}
|
||
.card-note { font-size: 12px; color: var(--brown-light); margin-bottom: 18px; }
|
||
.card-ingredients { list-style: none; margin-bottom: 20px; }
|
||
.card-ingredients li {
|
||
display: flex; align-items: center;
|
||
padding: 9px 0; border-bottom: 1px solid rgba(180,150,100,0.15);
|
||
font-size: 14px;
|
||
}
|
||
.card-ingredients li:last-child { border-bottom: none; }
|
||
.card-oil-name { flex: 1; color: var(--text-dark); font-weight: 500; }
|
||
.card-oil-drops { width: 60px; text-align: right; color: var(--sage-dark); font-size: 13px; }
|
||
.card-oil-cost { width: 70px; text-align: right; color: var(--text-light); font-size: 12px; }
|
||
.card-total {
|
||
background: linear-gradient(135deg, var(--sage), #5a7d5e);
|
||
border-radius: 12px; padding: 14px 20px;
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
margin-top: 8px;
|
||
}
|
||
.card-total-label { color: rgba(255,255,255,0.85); font-size: 13px; letter-spacing: 1px; }
|
||
.card-total-price { color: white; font-size: 20px; font-weight: 700; }
|
||
.card-footer {
|
||
margin-top: 16px; text-align: center;
|
||
font-size: 11px; color: var(--text-light); letter-spacing: 1px;
|
||
}
|
||
|
||
/* Manage section */
|
||
.manage-list { display: flex; flex-direction: column; gap: 12px; }
|
||
.manage-item {
|
||
background: white; border-radius: 14px; padding: 18px 22px;
|
||
box-shadow: var(--shadow); display: flex;
|
||
justify-content: space-between; align-items: center;
|
||
gap: 12px; flex-wrap: wrap;
|
||
}
|
||
.manage-item-left { flex: 1; }
|
||
.manage-item-name { font-weight: 600; font-size: 16px; color: var(--text-dark); }
|
||
.manage-item-oils { font-size: 13px; color: var(--text-light); margin-top: 4px; }
|
||
.manage-item-actions { display: flex; gap: 8px; flex-shrink: 0; flex-wrap: wrap; }
|
||
|
||
/* Add recipe form */
|
||
.form-card {
|
||
background: white; border-radius: 16px;
|
||
padding: 28px; box-shadow: var(--shadow); margin-bottom: 24px;
|
||
}
|
||
.form-title { font-family: 'Noto Serif SC', serif; font-size: 18px; font-weight: 600; margin-bottom: 20px; color: var(--text-dark); }
|
||
.form-group { margin-bottom: 16px; }
|
||
.form-label { font-size: 13px; color: var(--text-mid); margin-bottom: 6px; display: block; font-weight: 500; }
|
||
.form-control {
|
||
width: 100%; padding: 10px 14px;
|
||
border: 1.5px solid var(--border); border-radius: 10px;
|
||
font-size: 14px; font-family: inherit; outline: none;
|
||
transition: border-color 0.2s; background: white;
|
||
}
|
||
.form-control:focus { border-color: var(--sage); }
|
||
|
||
.new-ing-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
|
||
.new-ing-row {
|
||
display: flex; gap: 8px; align-items: center;
|
||
}
|
||
.new-ing-row select { flex: 1; }
|
||
.new-ing-row input { width: 80px; }
|
||
|
||
/* Oils section */
|
||
.oils-search { margin-bottom: 16px; }
|
||
.oils-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 10px;
|
||
}
|
||
.oil-chip {
|
||
background: white; border-radius: 10px; padding: 12px 16px;
|
||
box-shadow: 0 2px 8px rgba(90,60,30,0.06);
|
||
display: flex; justify-content: space-between; align-items: center;
|
||
gap: 8px;
|
||
}
|
||
.oil-chip-name { font-size: 14px; color: var(--text-dark); font-weight: 500; }
|
||
.oil-chip-price { font-size: 13px; color: var(--sage-dark); font-weight: 600; }
|
||
.oil-chip-actions { display: flex; gap: 4px; }
|
||
.oil-chip-btn {
|
||
background: none; border: none; cursor: pointer;
|
||
font-size: 13px; padding: 3px 6px; border-radius: 6px;
|
||
transition: background 0.2s; color: var(--text-light);
|
||
}
|
||
.oil-chip-btn:hover { background: var(--sage-mist); color: var(--sage-dark); }
|
||
.oil-chip-btn.del:hover { background: #fdf0ee; color: #c0392b; }
|
||
.oil-edit-input {
|
||
width: 90px; padding: 4px 8px; border: 1.5px solid var(--sage);
|
||
border-radius: 6px; font-size: 13px; font-family: inherit;
|
||
text-align: center; outline: none;
|
||
}
|
||
.add-oil-form {
|
||
background: white; border-radius: 14px; padding: 16px 20px;
|
||
box-shadow: var(--shadow); margin-bottom: 16px;
|
||
display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
|
||
}
|
||
.add-oil-form input {
|
||
padding: 9px 14px; border: 1.5px solid var(--border);
|
||
border-radius: 8px; font-size: 14px; font-family: inherit; outline: none;
|
||
}
|
||
.add-oil-form input:focus { border-color: var(--sage); }
|
||
|
||
/* Empty state */
|
||
.empty-state { text-align: center; padding: 60px 20px; color: var(--text-light); }
|
||
.empty-state-icon { font-size: 48px; margin-bottom: 12px; }
|
||
.empty-state-text { font-size: 15px; }
|
||
|
||
/* Tag */
|
||
.tag {
|
||
display: inline-block; padding: 3px 10px;
|
||
border-radius: 20px; font-size: 12px;
|
||
background: var(--sage-mist); color: var(--sage-dark);
|
||
margin: 2px;
|
||
}
|
||
.tag-btn {
|
||
display: inline-flex; align-items: center; gap: 4px;
|
||
padding: 5px 12px; border-radius: 20px; font-size: 13px;
|
||
background: var(--sage-mist); color: var(--sage-dark);
|
||
border: 1.5px solid transparent; cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
.tag-btn:hover { border-color: var(--sage); }
|
||
.tag-btn.active { background: var(--sage); color: white; border-color: var(--sage); }
|
||
.tag-btn .tag-del {
|
||
font-size: 14px; margin-left: 2px; opacity: 0.5;
|
||
cursor: pointer; border: none; background: none;
|
||
color: inherit; padding: 0 2px;
|
||
}
|
||
.tag-btn .tag-del:hover { opacity: 1; }
|
||
/* Tag picker overlay */
|
||
.tag-picker {
|
||
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
|
||
background: rgba(0,0,0,0.3); z-index: 999;
|
||
display: flex; align-items: center; justify-content: center;
|
||
}
|
||
.tag-picker-card {
|
||
background: white; border-radius: 16px; padding: 24px;
|
||
box-shadow: 0 8px 40px rgba(0,0,0,0.2); max-width: 400px; width: 90%;
|
||
}
|
||
.tag-picker-title { font-family: 'Noto Serif SC', serif; font-size: 16px; font-weight: 600; margin-bottom: 14px; }
|
||
.tag-picker-tags { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
|
||
.tag-pick {
|
||
padding: 6px 14px; border-radius: 20px; font-size: 13px;
|
||
border: 1.5px solid var(--border); background: white;
|
||
color: var(--text-mid); cursor: pointer; transition: all 0.15s;
|
||
}
|
||
.tag-pick:hover { border-color: var(--sage); }
|
||
.tag-pick.selected { background: var(--sage); color: white; border-color: var(--sage); }
|
||
|
||
/* Tooltip / hint */
|
||
.hint { font-size: 12px; color: var(--text-light); margin-top: 6px; }
|
||
|
||
.section-title {
|
||
font-family: 'Noto Serif SC', serif;
|
||
font-size: 18px; font-weight: 600; color: var(--text-dark);
|
||
margin-bottom: 16px; display: flex; align-items: center; gap: 8px;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.main { padding: 16px; }
|
||
.detail-panel { padding: 20px; }
|
||
.recipe-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="app-header">
|
||
<div class="header-inner">
|
||
<div class="header-icon">🌿</div>
|
||
<div class="header-title">
|
||
<h1>DOTERRA 精油配方计算器(Hera)</h1>
|
||
<p>查询配方 · 计算成本 · 导出卡片</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="nav-tabs">
|
||
<div class="nav-tab active" onclick="showSection('search')">🔍 配方查询</div>
|
||
<div class="nav-tab" onclick="showSection('manage')">📋 管理配方</div>
|
||
<div class="nav-tab" onclick="showSection('add')">➕ 新增配方</div>
|
||
<div class="nav-tab" onclick="showSection('oils')">💧 精油价目</div>
|
||
</div>
|
||
|
||
<div class="main">
|
||
|
||
<!-- ========== SEARCH SECTION ========== -->
|
||
<div class="section active" id="section-search">
|
||
<div class="search-box">
|
||
<div class="search-label">搜索配方名称或关键词</div>
|
||
<div class="search-row">
|
||
<input type="text" class="search-input" id="searchInput" placeholder="输入配方名称,如:白发转黑、痘痘、一夜好眠…" oninput="filterRecipes()">
|
||
<button class="btn btn-outline" onclick="clearSearch()">清除</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="recipeGrid" class="recipe-grid"></div>
|
||
|
||
<div id="detailPanel" style="display:none">
|
||
<div class="detail-panel">
|
||
<div class="detail-header">
|
||
<div>
|
||
<div class="detail-title" id="detailTitle"></div>
|
||
<div id="detailNote"></div>
|
||
</div>
|
||
<div class="detail-actions">
|
||
<button class="btn btn-primary btn-sm" onclick="addIngredientRow()">+ 加精油</button>
|
||
<button class="btn btn-gold btn-sm" onclick="exportCard()">📷 导出图片</button>
|
||
</div>
|
||
</div>
|
||
|
||
<table class="ingredients-table">
|
||
<thead>
|
||
<tr>
|
||
<th>精油</th>
|
||
<th>滴数</th>
|
||
<th>单价/滴</th>
|
||
<th>小计</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="ingredientsBody"></tbody>
|
||
</table>
|
||
|
||
<div class="add-ingredient-row" id="addIngRow" style="display:none">
|
||
<select id="newOilSelect" class="form-control" style="max-width:180px">
|
||
<option value="">— 选择精油 —</option>
|
||
</select>
|
||
<input type="number" id="newOilDrops" placeholder="滴数" style="width:90px" class="form-control" min="0.5" step="0.5">
|
||
<button class="btn btn-primary btn-sm" onclick="confirmAddIngredient()">确认添加</button>
|
||
<button class="btn btn-outline btn-sm" onclick="hideAddRow()">取消</button>
|
||
</div>
|
||
|
||
<div class="total-row">
|
||
<div class="total-label">配方总成本</div>
|
||
<div class="total-price" id="totalPrice">¥ 0.00</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Card Preview -->
|
||
<div class="card-preview-wrapper">
|
||
<div class="section-title">📋 配方卡片预览</div>
|
||
<div id="recipe-card-export">
|
||
<div class="card-brand">DOTERRA · 精油配方</div>
|
||
<div class="card-title" id="cardTitle"></div>
|
||
<div class="card-divider"></div>
|
||
<div class="card-note" id="cardNote"></div>
|
||
<ul class="card-ingredients" id="cardIngredients"></ul>
|
||
<div class="card-total">
|
||
<div class="card-total-label">配方总成本</div>
|
||
<div class="card-total-price" id="cardTotal"></div>
|
||
</div>
|
||
<div class="card-footer" id="cardDate"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ========== MANAGE SECTION ========== -->
|
||
<div class="section" id="section-manage">
|
||
<!-- Tag management bar -->
|
||
<div class="search-box" style="margin-bottom:16px">
|
||
<div class="search-label">🏷 标签管理 <span style="font-size:11px;color:var(--text-light)">(点击标签可筛选配方)</span></div>
|
||
<div id="tagBar" style="display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:8px"></div>
|
||
<div style="display:flex;gap:8px;margin-top:10px;align-items:center">
|
||
<input type="text" class="search-input" id="newTagInput" placeholder="新建标签…" style="flex:1;max-width:200px;padding:8px 12px;font-size:13px"
|
||
onkeydown="if(event.key==='Enter')addGlobalTag()">
|
||
<button class="btn btn-outline btn-sm" onclick="addGlobalTag()">+ 添加标签</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-title" style="justify-content:space-between;flex-wrap:wrap">
|
||
<span>📋 配方列表 <span id="manageFilterLabel" style="font-size:13px;font-weight:400;color:var(--text-light)"></span></span>
|
||
<div style="display:flex;gap:8px">
|
||
<button class="btn btn-outline btn-sm" onclick="toggleSelectAll()">全选/取消</button>
|
||
<button class="btn btn-gold btn-sm" onclick="exportExcel()">📥 导出 Excel</button>
|
||
</div>
|
||
</div>
|
||
<div id="manageList" class="manage-list"></div>
|
||
</div>
|
||
|
||
<!-- ========== ADD SECTION ========== -->
|
||
<div class="section" id="section-add">
|
||
<div class="form-card">
|
||
<div class="form-title">✨ 智能粘贴</div>
|
||
<div class="form-group">
|
||
<label class="form-label">直接输入或粘贴配方,第一个词为配方名,后面跟精油名+滴数</label>
|
||
<textarea class="form-control" id="smartPasteInput" rows="3" placeholder="例:长高,芳香调理8,永久花10,檀香10,乳香15,西伯利亚冷杉20"></textarea>
|
||
<div class="hint">支持逗号、顿号、换行分隔,也支持无分隔直接连写(如:芳香调理8永久花10檀香10),自动用数字断句。不需要写椰子油,系统会按稀释比例自动计算。</div>
|
||
</div>
|
||
<div style="display:flex; gap:12px;">
|
||
<button class="btn btn-primary" onclick="smartPaste()">🪄 识别并生成</button>
|
||
<button class="btn btn-outline" onclick="document.getElementById('smartPasteInput').value=''">清除</button>
|
||
</div>
|
||
<div id="smartPasteResult" style="margin-top:12px"></div>
|
||
</div>
|
||
|
||
<div class="form-card">
|
||
<div class="form-title">➕ 手动新增配方</div>
|
||
<div class="form-group">
|
||
<label class="form-label">配方名称 *</label>
|
||
<input type="text" class="form-control" id="newRecipeName" placeholder="如:淡斑精华、改善睡眠…">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">备注说明(可选)</label>
|
||
<input type="text" class="form-control" id="newRecipeNote" placeholder="如:适合晚间使用,有艾草时可加入">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label">精油成分 *</label>
|
||
<div class="new-ing-list" id="newIngList"></div>
|
||
<button class="btn btn-outline btn-sm" onclick="addNewIngRow()">+ 添加精油</button>
|
||
</div>
|
||
<div style="margin-top: 24px; display:flex; gap:12px;">
|
||
<button class="btn btn-primary" onclick="saveNewRecipe()">💾 保存配方</button>
|
||
<button class="btn btn-outline" onclick="clearNewForm()">重置</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ========== OILS SECTION ========== -->
|
||
<div class="section" id="section-oils">
|
||
<div class="section-title">💧 精油价目表 <span style="font-size:14px;font-weight:400;color:var(--text-light)">共 <span id="oilCount"></span> 种精油</span></div>
|
||
<div class="add-oil-form">
|
||
<input type="text" id="newOilName" placeholder="精油名称" style="flex:1;min-width:120px">
|
||
<input type="number" id="newOilBottlePrice" placeholder="一瓶价格 (¥)" style="width:130px" step="0.01" min="0">
|
||
<select id="newOilVolume" class="form-control" style="width:110px" onchange="onVolumeChange('new')">
|
||
<option value="">容量</option>
|
||
<option value="2.5">2.5ml (46滴)</option>
|
||
<option value="5">5ml (93滴)</option>
|
||
<option value="10">10ml (186滴)</option>
|
||
<option value="15">15ml (280滴)</option>
|
||
<option value="115">115ml (2146滴)</option>
|
||
<option value="custom">自定义滴数</option>
|
||
</select>
|
||
<input type="number" id="newOilDropCount" placeholder="总滴数" style="width:90px;display:none" step="1" min="1">
|
||
<button class="btn btn-primary btn-sm" onclick="addNewOil()">➕ 添加精油</button>
|
||
</div>
|
||
<div class="search-box oils-search">
|
||
<input type="text" class="search-input" id="oilSearchInput" placeholder="搜索精油名称…" oninput="filterOils()" style="width:100%">
|
||
</div>
|
||
<div id="oilsGrid" class="oils-grid"></div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
// ============ DATA ============
|
||
const DEFAULT_OILS = {"小豆蔻": 2.69, "芹菜籽": 1.11, "芫荽": 0.79, "小茴香": 0.52, "生姜": 1.48, "姜黄": 0.98, "缬草": 1.73, "岩兰草": 1.86, "侧柏": 2.1, "桦木": 5.11, "雪松": 0.46, "斯里兰卡肉桂皮": 2.96, "夏威夷檀香": 6.61, "檀香": 7.69, "古巴香脂": 1.0, "乳香": 2.25, "枫香": 0.86, "没药": 2.09, "罗勒": 0.96, "黑云杉": 1.83, "芫荽叶": 0.82, "香茅": 0.61, "丝柏": 0.55, "道格拉斯冷杉": 2.1, "尤加利": 0.66, "扁柏": 2.47, "柠檬尤加利": 0.43, "柠檬草": 0.41, "马郁兰": 0.7, "香蜂草": 8.71, "麦卢卡": 4.62, "西班牙牛至": 0.8, "广藿香": 0.96, "椒样薄荷": 0.75, "苦橙叶": 0.79, "迷迭香": 0.63, "西伯利亚冷杉": 0.61, "西班牙鼠尾草": 0.82, "绿薄荷": 0.89, "茶树": 0.7, "百里香": 1.0, "冬青": 0.84, "蓝艾菊": 7.53, "快乐鼠尾草": 1.16, "丁香花蕾": 0.61, "天竺葵": 1.38, "永久花": 7.15, "茉莉": 26.3, "薰衣草": 0.82, "罗马洋甘菊": 4.52, "依兰依兰": 1.25, "佛手柑": 1.23, "黑胡椒": 2.04, "圆柚": 0.59, "杜松浆果": 2.04, "柠檬": 0.43, "莱姆": 0.48, "山鸡椒": 0.68, "加州胡椒": 2.04, "红橘": 0.46, "野橘": 0.38, "乐释": 1.25, "赋活呼吸": 0.95, "芳香调理": 0.98, "安定情绪": 0.73, "柑橘清新": 0.68, "柑橘绚烂": 0.82, "温柔呵护": 1.59, "完美修护": 1.14, "舒缓": 3.28, "乐活": 1.09, "顺畅呼吸": 0.8, "愈创木": 0.57, "恬家": 0.79, "安宁神气": 1.2, "椰风香草": 3.33, "清醇薄荷": 0.91, "新瑞活力": 0.86, "保卫": 1.13, "净化清新": 0.73, "花样年华焕肤油": 3.66, "天然防护": 0.39, "西洋蓍草": 1.61, "元气": 0.82, "欢欣": 2.31, "抚慰": 3.6, "宽容": 2.1, "鼓舞": 2.2, "热情": 3.82, "静谧": 3.01, "椰子油": 0.05, "植物空胶囊": 0.12, "玫瑰": 28.82, "玫瑰呵护": 2.53, "茉莉呵护": 2.74, "橙花呵护": 2.31, "桂花呵护": 2.58, "木兰呵护": 2.04, "茶树呵护": 1.13, "特瑞活力": 1.25, "全神贯注": 1.72, "新清肌呵护": 1.4, "新清肌调理": 0.93, "当归": 4.84, "月桂叶": 1.0, "橙花": 12.37, "桂花": 10.54, "穗甘松": 4.84, "玫瑰草": 0.64, "罗文莎叶": 0.89, "甜茴香": 0.64, "五味子": 1.0, "印蒿": 1.14, "柠檬香桃木": 0.89};
|
||
const DEFAULT_OILS_META = {"小豆蔻": {"bottlePrice": 250, "dropCount": 93}, "芹菜籽": {"bottlePrice": 310, "dropCount": 280}, "芫荽": {"bottlePrice": 220, "dropCount": 280}, "小茴香": {"bottlePrice": 145, "dropCount": 280}, "生姜": {"bottlePrice": 415, "dropCount": 280}, "姜黄": {"bottlePrice": 275, "dropCount": 280}, "缬草": {"bottlePrice": 485, "dropCount": 280}, "岩兰草": {"bottlePrice": 520, "dropCount": 280}, "侧柏": {"bottlePrice": 195, "dropCount": 93}, "桦木": {"bottlePrice": 475, "dropCount": 93}, "雪松": {"bottlePrice": 130, "dropCount": 280}, "斯里兰卡肉桂皮": {"bottlePrice": 275, "dropCount": 93}, "夏威夷檀香": {"bottlePrice": 615, "dropCount": 93}, "檀香": {"bottlePrice": 715, "dropCount": 93}, "古巴香脂": {"bottlePrice": 279, "dropCount": 280}, "乳香": {"bottlePrice": 630, "dropCount": 280}, "枫香": {"bottlePrice": 240, "dropCount": 280}, "没药": {"bottlePrice": 585, "dropCount": 280}, "罗勒": {"bottlePrice": 270, "dropCount": 280}, "黑云杉": {"bottlePrice": 170, "dropCount": 93}, "芫荽叶": {"bottlePrice": 230, "dropCount": 280}, "香茅": {"bottlePrice": 170, "dropCount": 280}, "丝柏": {"bottlePrice": 155, "dropCount": 280}, "道格拉斯冷杉": {"bottlePrice": 195, "dropCount": 93}, "尤加利": {"bottlePrice": 185, "dropCount": 280}, "扁柏": {"bottlePrice": 230, "dropCount": 93}, "柠檬尤加利": {"bottlePrice": 120, "dropCount": 280}, "柠檬草": {"bottlePrice": 115, "dropCount": 280}, "马郁兰": {"bottlePrice": 195, "dropCount": 280}, "香蜂草": {"bottlePrice": 810, "dropCount": 93}, "麦卢卡": {"bottlePrice": 430, "dropCount": 93}, "西班牙牛至": {"bottlePrice": 225, "dropCount": 280}, "广藿香": {"bottlePrice": 270, "dropCount": 280}, "椒样薄荷": {"bottlePrice": 210, "dropCount": 280}, "苦橙叶": {"bottlePrice": 220, "dropCount": 280}, "迷迭香": {"bottlePrice": 175, "dropCount": 280}, "西伯利亚冷杉": {"bottlePrice": 170, "dropCount": 280}, "西班牙鼠尾草": {"bottlePrice": 230, "dropCount": 280}, "绿薄荷": {"bottlePrice": 250, "dropCount": 280}, "茶树": {"bottlePrice": 195, "dropCount": 280}, "百里香": {"bottlePrice": 280, "dropCount": 280}, "冬青": {"bottlePrice": 235, "dropCount": 280}, "蓝艾菊": {"bottlePrice": 700, "dropCount": 93}, "快乐鼠尾草": {"bottlePrice": 325, "dropCount": 280}, "丁香花蕾": {"bottlePrice": 170, "dropCount": 280}, "天竺葵": {"bottlePrice": 385, "dropCount": 280}, "永久花": {"bottlePrice": 665, "dropCount": 93}, "茉莉": {"bottlePrice": 1210, "dropCount": 46}, "薰衣草": {"bottlePrice": 230, "dropCount": 280}, "罗马洋甘菊": {"bottlePrice": 420, "dropCount": 93}, "依兰依兰": {"bottlePrice": 350, "dropCount": 280}, "佛手柑": {"bottlePrice": 345, "dropCount": 280}, "黑胡椒": {"bottlePrice": 190, "dropCount": 93}, "圆柚": {"bottlePrice": 165, "dropCount": 280}, "杜松浆果": {"bottlePrice": 190, "dropCount": 93}, "柠檬": {"bottlePrice": 120, "dropCount": 280}, "莱姆": {"bottlePrice": 135, "dropCount": 280}, "山鸡椒": {"bottlePrice": 190, "dropCount": 280}, "加州胡椒": {"bottlePrice": 190, "dropCount": 93}, "红橘": {"bottlePrice": 130, "dropCount": 280}, "野橘": {"bottlePrice": 105, "dropCount": 280}, "乐释": {"bottlePrice": 350, "dropCount": 280}, "赋活呼吸": {"bottlePrice": 265, "dropCount": 280}, "芳香调理": {"bottlePrice": 275, "dropCount": 280}, "安定情绪": {"bottlePrice": 205, "dropCount": 280}, "柑橘清新": {"bottlePrice": 190, "dropCount": 280}, "柑橘绚烂": {"bottlePrice": 230, "dropCount": 280}, "温柔呵护": {"bottlePrice": 445, "dropCount": 280}, "完美修护": {"bottlePrice": 320, "dropCount": 280}, "舒缓": {"bottlePrice": 305, "dropCount": 93}, "乐活": {"bottlePrice": 305, "dropCount": 280}, "顺畅呼吸": {"bottlePrice": 225, "dropCount": 280}, "愈创木": {"bottlePrice": 160, "dropCount": 280}, "恬家": {"bottlePrice": 220, "dropCount": 280}, "安宁神气": {"bottlePrice": 335, "dropCount": 280}, "椰风香草": {"bottlePrice": 310, "dropCount": 93}, "清醇薄荷": {"bottlePrice": 255, "dropCount": 280}, "新瑞活力": {"bottlePrice": 240, "dropCount": 280}, "保卫": {"bottlePrice": 315, "dropCount": 280}, "净化清新": {"bottlePrice": 205, "dropCount": 280}, "花样年华焕肤油": {"bottlePrice": 680, "dropCount": 186}, "天然防护": {"bottlePrice": 110, "dropCount": 280}, "西洋蓍草": {"bottlePrice": 450, "dropCount": 280}, "元气": {"bottlePrice": 230, "dropCount": 280}, "欢欣": {"bottlePrice": 215, "dropCount": 93}, "抚慰": {"bottlePrice": 335, "dropCount": 93}, "宽容": {"bottlePrice": 195, "dropCount": 93}, "鼓舞": {"bottlePrice": 205, "dropCount": 93}, "热情": {"bottlePrice": 355, "dropCount": 93}, "静谧": {"bottlePrice": 280, "dropCount": 93}, "椰子油": {"bottlePrice": 115, "dropCount": 2146}, "植物空胶囊": {"bottlePrice": 32.73, "dropCount": 280}, "玫瑰": {"bottlePrice": 2680, "dropCount": 93}, "玫瑰呵护": {"bottlePrice": 470, "dropCount": 186}, "茉莉呵护": {"bottlePrice": 510, "dropCount": 186}, "橙花呵护": {"bottlePrice": 430, "dropCount": 186}, "桂花呵护": {"bottlePrice": 480, "dropCount": 186}, "木兰呵护": {"bottlePrice": 380, "dropCount": 186}, "茶树呵护": {"bottlePrice": 210, "dropCount": 186}, "特瑞活力": {"bottlePrice": 350, "dropCount": 280}, "全神贯注": {"bottlePrice": 320, "dropCount": 186}, "新清肌呵护": {"bottlePrice": 260, "dropCount": 186}, "新清肌调理": {"bottlePrice": 260, "dropCount": 280}, "当归": {"bottlePrice": 450, "dropCount": 93}, "月桂叶": {"bottlePrice": 280, "dropCount": 280}, "橙花": {"bottlePrice": 1150, "dropCount": 93}, "桂花": {"bottlePrice": 980, "dropCount": 93}, "穗甘松": {"bottlePrice": 450, "dropCount": 93}, "玫瑰草": {"bottlePrice": 180, "dropCount": 280}, "罗文莎叶": {"bottlePrice": 250, "dropCount": 280}, "甜茴香": {"bottlePrice": 180, "dropCount": 280}, "五味子": {"bottlePrice": 280, "dropCount": 280}, "印蒿": {"bottlePrice": 320, "dropCount": 280}, "柠檬香桃木": {"bottlePrice": 250, "dropCount": 280}};
|
||
|
||
// OILS stores per-drop price for calculations
|
||
// OILS_META stores {bottlePrice, dropCount} for editing
|
||
let OILS = {};
|
||
let OILS_META = {};
|
||
|
||
function loadOils() {
|
||
try {
|
||
const savedMeta = localStorage.getItem('doterra_oils_meta');
|
||
if (savedMeta) {
|
||
OILS_META = JSON.parse(savedMeta);
|
||
// Merge any new oils from DEFAULT_OILS_META
|
||
for (const [name, meta] of Object.entries(DEFAULT_OILS_META)) {
|
||
if (!(name in OILS_META)) {
|
||
OILS_META[name] = JSON.parse(JSON.stringify(meta));
|
||
}
|
||
}
|
||
// Rebuild per-drop prices from meta
|
||
OILS = {};
|
||
for (const [name, meta] of Object.entries(OILS_META)) {
|
||
OILS[name] = meta.bottlePrice / meta.dropCount;
|
||
}
|
||
saveOils();
|
||
} else {
|
||
OILS = JSON.parse(JSON.stringify(DEFAULT_OILS));
|
||
OILS_META = JSON.parse(JSON.stringify(DEFAULT_OILS_META));
|
||
saveOils();
|
||
}
|
||
} catch(e) {
|
||
OILS = JSON.parse(JSON.stringify(DEFAULT_OILS));
|
||
}
|
||
}
|
||
|
||
function saveOils() {
|
||
localStorage.setItem('doterra_oils_meta', JSON.stringify(OILS_META));
|
||
}
|
||
|
||
const DEFAULT_RECIPES = [
|
||
{name:"酸痛包",note:"",ingredients:[{oil:"椒样薄荷",drops:1},{oil:"舒缓",drops:2},{oil:"芳香调理",drops:1},{oil:"冬青",drops:1},{oil:"柠檬草",drops:1},{oil:"生姜",drops:2},{oil:"茶树",drops:1},{oil:"乳香",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"小v脸",note:"",ingredients:[{oil:"丝柏",drops:2},{oil:"乳香",drops:1},{oil:"西洋蓍草",drops:1},{oil:"永久花",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"健脾化湿精油浴",note:"",ingredients:[{oil:"西伯利亚冷杉",drops:3},{oil:"芫荽",drops:3},{oil:"红橘",drops:2},{oil:"椰子油",drops:20}]},
|
||
{name:"一夜好眠精油浴",note:"",ingredients:[{oil:"安宁神气",drops:1},{oil:"岩兰草",drops:1},{oil:"乐释",drops:1},{oil:"薰衣草",drops:1},{oil:"乳香",drops:1},{oil:"安定情绪",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"生发",note:"",ingredients:[{oil:"椒样薄荷",drops:1},{oil:"茶树",drops:1},{oil:"迷迭香",drops:1},{oil:"丝柏",drops:2},{oil:"生姜",drops:1},{oil:"雪松",drops:2},{oil:"薰衣草",drops:1},{oil:"乳香",drops:2},{oil:"安定情绪",drops:1},{oil:"椰子油",drops:15}]},
|
||
{name:"湿疹舒缓",note:"",ingredients:[{oil:"广藿香",drops:1},{oil:"绿薄荷",drops:1},{oil:"麦卢卡",drops:1},{oil:"永久花",drops:1},{oil:"蓝艾菊",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"乳腺疏通",note:"",ingredients:[{oil:"乳香",drops:1},{oil:"薰衣草",drops:1},{oil:"丁香花蕾",drops:1},{oil:"柑橘清新",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"缓解酸痛精油刮痧",note:"",ingredients:[{oil:"椒样薄荷",drops:1},{oil:"舒缓",drops:2},{oil:"芳香调理",drops:1},{oil:"冬青",drops:1},{oil:"柠檬草",drops:1},{oil:"生姜",drops:2},{oil:"茶树",drops:1},{oil:"乳香",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"灰指甲",note:"",ingredients:[{oil:"西班牙牛至",drops:1},{oil:"椰子油",drops:6}]},
|
||
{name:"白发转黑",note:"",ingredients:[{oil:"乳香",drops:2},{oil:"快乐鼠尾草",drops:1},{oil:"依兰依兰",drops:1},{oil:"生姜",drops:1},{oil:"薰衣草",drops:1},{oil:"扁柏",drops:1},{oil:"雪松",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"紫外线修复",note:"有艾草时可加入艾草",ingredients:[{oil:"乳香",drops:1},{oil:"西洋蓍草",drops:1},{oil:"蓝艾菊",drops:1},{oil:"麦卢卡",drops:1},{oil:"侧柏",drops:1},{oil:"椰子油",drops:15}]},
|
||
{name:"瘦身带脉",note:"",ingredients:[{oil:"丝柏",drops:1},{oil:"圆柚",drops:1},{oil:"新瑞活力",drops:1},{oil:"永久花",drops:1},{oil:"黑胡椒",drops:1},{oil:"姜黄",drops:1},{oil:"柠檬",drops:1},{oil:"乳香",drops:1},{oil:"椰子油",drops:15}]},
|
||
{name:"私密护理",note:"",ingredients:[{oil:"西洋蓍草",drops:2},{oil:"没药",drops:2},{oil:"快乐鼠尾草",drops:1},{oil:"依兰依兰",drops:1},{oil:"茶树",drops:0.5},{oil:"丝柏",drops:0.5},{oil:"椰子油",drops:8},{oil:"植物空胶囊",drops:1}]},
|
||
{name:"脚气/头皮屑",note:"",ingredients:[{oil:"茶树",drops:1},{oil:"椰子油",drops:6}]},
|
||
{name:"痘痘",note:"",ingredients:[{oil:"广藿香",drops:1},{oil:"姜黄",drops:1},{oil:"西洋蓍草",drops:2},{oil:"茶树",drops:1},{oil:"麦卢卡",drops:1},{oil:"椰子油",drops:20}]},
|
||
{name:"淋巴排毒",note:"",ingredients:[{oil:"乳香",drops:1},{oil:"丝柏",drops:1},{oil:"圆柚",drops:1},{oil:"迷迭香",drops:1},{oil:"杜松浆果",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"驱寒/祛湿精油浴",note:"有艾草时可加入",ingredients:[{oil:"杜松浆果",drops:3},{oil:"广藿香",drops:3},{oil:"生姜",drops:2},{oil:"椰子油",drops:20}]},
|
||
{name:"荷尔蒙调节/更年期护理精油浴",note:"",ingredients:[{oil:"依兰依兰",drops:2},{oil:"温柔呵护",drops:2},{oil:"天竺葵",drops:2},{oil:"快乐鼠尾草",drops:2},{oil:"岩兰草",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"情绪管理",note:"",ingredients:[{oil:"抚慰",drops:1},{oil:"热情",drops:1},{oil:"欢欣",drops:1},{oil:"鼓舞",drops:1},{oil:"宽容",drops:1},{oil:"静谧",drops:1},{oil:"椰子油",drops:10}]},
|
||
{name:"发膜",note:"",ingredients:[{oil:"乳香",drops:2},{oil:"茶树",drops:2},{oil:"生姜",drops:2}]},
|
||
{name:"脾胃养护1",note:"",tags:[],ingredients:[{oil:"生姜",drops:10},{oil:"红橘",drops:20},{oil:"乐活",drops:20},{oil:"广藿香",drops:15},{oil:"岩兰草",drops:10}]},
|
||
{name:"脾胃养护2",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"红橘",drops:10},{oil:"生姜",drops:10},{oil:"乐活",drops:15}]},
|
||
{name:"招财开运油",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:1},{oil:"岩兰草",drops:1},{oil:"天竺葵",drops:2},{oil:"佛手柑",drops:10},{oil:"野橘",drops:20}]},
|
||
{name:"植物热玛吉(玫瑰纯油版)",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:20},{oil:"玫瑰",drops:5},{oil:"丝柏",drops:10},{oil:"永久花",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"植物热玛吉(玫瑰呵护版)",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:20},{oil:"玫瑰",drops:5},{oil:"丝柏",drops:10},{oil:"永久花",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"十全大补",note:"",tags:[],ingredients:[{oil:"快乐鼠尾草",drops:15},{oil:"花样年华焕肤油",drops:10},{oil:"温柔呵护",drops:8},{oil:"玫瑰呵护",drops:15},{oil:"茉莉呵护",drops:15},{oil:"温柔呵护",drops:10}]},
|
||
{name:"豪华头疗",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:25},{oil:"丝柏",drops:15},{oil:"夏威夷檀香",drops:10},{oil:"完美修护",drops:20},{oil:"乳香",drops:20},{oil:"生姜",drops:15},{oil:"雪松",drops:15},{oil:"马郁兰",drops:10},{oil:"安定情绪",drops:20},{oil:"依兰依兰",drops:10}]},
|
||
{name:"酸痛包1",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"舒缓",drops:10},{oil:"芳香调理",drops:5},{oil:"冬青",drops:10},{oil:"柠檬草",drops:5},{oil:"生姜",drops:5},{oil:"西班牙牛至",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"酸痛包2",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"舒缓",drops:5},{oil:"芳香调理",drops:5},{oil:"柠檬草",drops:5},{oil:"生姜",drops:5},{oil:"茶树",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"明目青睐1",note:"",tags:[],ingredients:[{oil:"柠檬草",drops:6},{oil:"乳香",drops:10},{oil:"永久花",drops:9},{oil:"花样年华焕肤油",drops:10},{oil:"快乐鼠尾草",drops:10}]},
|
||
{name:"明目青睐2",note:"",tags:[],ingredients:[{oil:"芳香调理",drops:3},{oil:"柠檬草",drops:3},{oil:"快乐鼠尾草",drops:7},{oil:"乳香",drops:7}]},
|
||
{name:"带脉排毒瘦腰1",note:"",tags:[],ingredients:[{oil:"广藿香",drops:5},{oil:"黑胡椒",drops:10},{oil:"天竺葵",drops:20},{oil:"新瑞活力",drops:30},{oil:"丝柏",drops:30},{oil:"乳香",drops:20},{oil:"杜松浆果",drops:20}]},
|
||
{name:"带脉瘦腰2",note:"",tags:[],ingredients:[{oil:"杜松浆果",drops:5},{oil:"生姜",drops:5},{oil:"圆柚",drops:10},{oil:"丝柏",drops:10},{oil:"乳香",drops:7},{oil:"天竺葵",drops:5},{oil:"新瑞活力",drops:10}]},
|
||
{name:"缓解头痛(强)",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:10},{oil:"薰衣草",drops:10},{oil:"罗勒",drops:15},{oil:"马郁兰",drops:15},{oil:"舒缓",drops:50}]},
|
||
{name:"1清咽止咳(强)",note:"",tags:[],ingredients:[{oil:"没药",drops:10},{oil:"乳香",drops:15},{oil:"尤加利",drops:15},{oil:"小豆蔻",drops:10},{oil:"顺畅呼吸",drops:20},{oil:"西伯利亚冷杉",drops:10}]},
|
||
{name:"清咽止咳2",note:"",tags:[],ingredients:[{oil:"茶树",drops:5},{oil:"乳香",drops:10},{oil:"保卫",drops:5},{oil:"顺畅呼吸",drops:10}]},
|
||
{name:"提升免疫(强)",note:"",tags:[],ingredients:[{oil:"西班牙牛至",drops:5},{oil:"侧柏",drops:10},{oil:"百里香",drops:10},{oil:"柠檬草",drops:10},{oil:"乳香",drops:20},{oil:"茶树",drops:10},{oil:"保卫",drops:10}]},
|
||
{name:"护肝排毒(强)",note:"",tags:[],ingredients:[{oil:"柠檬",drops:20},{oil:"元气",drops:20},{oil:"当归",drops:1},{oil:"芹菜籽",drops:10},{oil:"天竺葵",drops:9},{oil:"迷迭香",drops:15}]},
|
||
{name:"强心护心(强)",note:"",tags:[],ingredients:[{oil:"百里香",drops:10},{oil:"香蜂草",drops:15},{oil:"古巴香脂",drops:20},{oil:"依兰依兰",drops:15},{oil:"快乐鼠尾草",drops:15}]},
|
||
{name:"通鼻消炎(强)1",note:"",tags:[],ingredients:[{oil:"茶树",drops:5},{oil:"椒样薄荷",drops:5},{oil:"乳香",drops:10},{oil:"尤加利",drops:10},{oil:"蓝艾菊",drops:5},{oil:"迷迭香",drops:10},{oil:"顺畅呼吸",drops:20}]},
|
||
{name:"通鼻消炎2",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"茶树",drops:5},{oil:"尤加利",drops:10},{oil:"顺畅呼吸",drops:15}]},
|
||
{name:"过敏湿疹(强)",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:10},{oil:"蓝艾菊",drops:20},{oil:"薰衣草",drops:10},{oil:"香蜂草",drops:15},{oil:"绿薄荷",drops:10},{oil:"永久花",drops:10},{oil:"广藿香",drops:15},{oil:"乳香",drops:10}]},
|
||
{name:"强肾化水(强)",note:"",tags:[],ingredients:[{oil:"茉莉呵护",drops:10},{oil:"檀香",drops:15},{oil:"黑胡椒",drops:10},{oil:"依兰依兰",drops:10},{oil:"古巴香脂",drops:10},{oil:"杜松浆果",drops:20}]},
|
||
{name:"防疫病毒(强)",note:"",tags:[],ingredients:[{oil:"尤加利",drops:5},{oil:"侧柏",drops:5},{oil:"百里香",drops:5},{oil:"茶树",drops:10},{oil:"保卫",drops:10}]},
|
||
{name:"消富贵包(强)",note:"",tags:[],ingredients:[{oil:"芳香调理",drops:10},{oil:"永久花",drops:10},{oil:"生姜",drops:10},{oil:"舒缓",drops:5},{oil:"柠檬草",drops:10},{oil:"完美修护",drops:10},{oil:"乳香",drops:10}]},
|
||
{name:"淋巴排毒(强)",note:"",tags:[],ingredients:[{oil:"柑橘清新",drops:5},{oil:"丁香花蕾",drops:10},{oil:"柠檬草",drops:5},{oil:"没药",drops:20},{oil:"乳香",drops:20}]},
|
||
{name:"祛湿化滞(强)",note:"",tags:[],ingredients:[{oil:"黑胡椒",drops:8},{oil:"斯里兰卡肉桂皮",drops:6},{oil:"圆柚",drops:9},{oil:"岩兰草",drops:8},{oil:"广藿香",drops:9},{oil:"姜黄",drops:10},{oil:"西洋蓍草",drops:10}]},
|
||
{name:"肺部结节",note:"",tags:[],ingredients:[{oil:"完美修护",drops:10},{oil:"乳香",drops:10},{oil:"椒样薄荷",drops:5},{oil:"柑橘清新",drops:5},{oil:"赋活呼吸",drops:5},{oil:"天竺葵",drops:10}]},
|
||
{name:"尿路感染1",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:10},{oil:"杜松浆果",drops:5},{oil:"西班牙牛至",drops:5},{oil:"柠檬草",drops:5},{oil:"罗勒",drops:5},{oil:"保卫",drops:5},{oil:"柠檬",drops:5}]},
|
||
{name:"尿路感染2",note:"",tags:[],ingredients:[{oil:"西班牙牛至",drops:1},{oil:"柠檬草",drops:1}]},
|
||
{name:"养前列腺",note:"",tags:[],ingredients:[{oil:"檀香",drops:3},{oil:"杜松浆果",drops:8},{oil:"斯里兰卡肉桂皮",drops:2},{oil:"芳香调理",drops:6},{oil:"茉莉",drops:1},{oil:"快乐鼠尾草",drops:3},{oil:"五味子",drops:15},{oil:"百里香",drops:2}]},
|
||
{name:"皮外损伤",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"茶树",drops:10},{oil:"薰衣草",drops:8},{oil:"没药",drops:4},{oil:"永久花",drops:4}]},
|
||
{name:"消脂肪肝",note:"",tags:[],ingredients:[{oil:"杜松浆果",drops:5},{oil:"元气",drops:5},{oil:"乳香",drops:2},{oil:"柠檬",drops:3},{oil:"柠檬草",drops:5},{oil:"新瑞活力",drops:10}]},
|
||
{name:"平衡血糖",note:"",tags:[],ingredients:[{oil:"新瑞活力",drops:10},{oil:"薰衣草",drops:5},{oil:"斯里兰卡肉桂皮",drops:3},{oil:"迷迭香",drops:3},{oil:"芫荽",drops:3},{oil:"山鸡椒",drops:3},{oil:"天竺葵",drops:3}]},
|
||
{name:"肚痛腹泻",note:"",tags:[],ingredients:[{oil:"乐活",drops:16},{oil:"柠檬草",drops:8},{oil:"生姜",drops:6},{oil:"西班牙牛至",drops:6},{oil:"罗勒",drops:4}]},
|
||
{name:"韧带扭伤",note:"",tags:[],ingredients:[{oil:"芳香调理",drops:8},{oil:"姜黄",drops:5},{oil:"柠檬草",drops:8},{oil:"西班牙牛至",drops:3},{oil:"永久花",drops:5},{oil:"罗勒",drops:2},{oil:"古巴香脂",drops:6}]},
|
||
{name:"血脂稳定",note:"",tags:[],ingredients:[{oil:"新瑞活力",drops:10},{oil:"柠檬草",drops:5},{oil:"永久花",drops:5},{oil:"小茴香",drops:5},{oil:"罗勒",drops:5},{oil:"元气",drops:5}]},
|
||
{name:"尿床尿频",note:"",tags:[],ingredients:[{oil:"杜松浆果",drops:16},{oil:"柠檬草",drops:12},{oil:"侧柏",drops:8},{oil:"檀香",drops:4}]},
|
||
{name:"冻疮修复",note:"",tags:[],ingredients:[{oil:"乳香",drops:5},{oil:"薰衣草",drops:5},{oil:"黑胡椒",drops:3},{oil:"没药",drops:3},{oil:"迷迭香",drops:5},{oil:"生姜",drops:5},{oil:"天竺葵",drops:10}]},
|
||
{name:"皮下囊肿",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:5},{oil:"乳香",drops:10},{oil:"百里香",drops:5},{oil:"西班牙牛至",drops:5},{oil:"广藿香",drops:10},{oil:"丁香花蕾",drops:5}]},
|
||
{name:"疏肝解郁",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:5},{oil:"芹菜籽",drops:2},{oil:"岩兰草",drops:5},{oil:"元气",drops:8},{oil:"佛手柑",drops:5},{oil:"罗马洋甘菊",drops:5}]},
|
||
{name:"大脑抗衰",note:"",tags:[],ingredients:[{oil:"特瑞活力",drops:10},{oil:"完美修护",drops:10},{oil:"乳香",drops:10},{oil:"迷迭香",drops:5},{oil:"乐释",drops:5},{oil:"广藿香",drops:5},{oil:"古巴香脂",drops:5}]},
|
||
{name:"神经麻木",note:"",tags:[],ingredients:[{oil:"特瑞活力",drops:10},{oil:"完美修护",drops:10},{oil:"永久花",drops:5},{oil:"百里香",drops:5},{oil:"古巴香脂",drops:10},{oil:"马郁兰",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"口唇疱疹1",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:5},{oil:"罗马洋甘菊",drops:7},{oil:"茶树",drops:5},{oil:"乳香",drops:5},{oil:"玫瑰草",drops:3},{oil:"没药",drops:5}]},
|
||
{name:"口唇疱疹2",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:6},{oil:"罗马洋甘菊",drops:6},{oil:"罗文莎叶",drops:6}]},
|
||
{name:"春季用油",note:"",tags:[],ingredients:[{oil:"黑云杉",drops:3},{oil:"柠檬草",drops:5},{oil:"杜松浆果",drops:5},{oil:"芹菜籽",drops:5},{oil:"天竺葵",drops:5},{oil:"莱姆",drops:10},{oil:"元气",drops:10}]},
|
||
{name:"夏季用油",note:"",tags:[],ingredients:[{oil:"生姜",drops:5},{oil:"杜松浆果",drops:5},{oil:"丝柏",drops:5},{oil:"圆柚",drops:5},{oil:"斯里兰卡肉桂皮",drops:3},{oil:"西伯利亚冷杉",drops:5},{oil:"广藿香",drops:7},{oil:"乐活",drops:10}]},
|
||
{name:"秋季用油",note:"",tags:[],ingredients:[{oil:"西伯利亚冷杉",drops:5},{oil:"茶树",drops:5},{oil:"小豆蔻",drops:3},{oil:"丁香花蕾",drops:2},{oil:"迷迭香",drops:3},{oil:"乳香",drops:7},{oil:"顺畅呼吸",drops:10}]},
|
||
{name:"冬季用油",note:"",tags:[],ingredients:[{oil:"茉莉呵护",drops:10},{oil:"生姜",drops:5},{oil:"雪松",drops:5},{oil:"黑胡椒",drops:2},{oil:"檀香",drops:3},{oil:"五味子",drops:3},{oil:"杜松浆果",drops:5},{oil:"天竺葵",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"祛湿丸子",note:"",tags:[],ingredients:[{oil:"斯里兰卡肉桂皮",drops:1},{oil:"圆柚",drops:2},{oil:"姜黄",drops:2},{oil:"黑胡椒",drops:1},{oil:"岩兰草",drops:2},{oil:"广藿香",drops:2},{oil:"西洋蓍草",drops:2}]},
|
||
{name:"三伏排湿",note:"",tags:[],ingredients:[{oil:"杜松浆果",drops:5},{oil:"芳香调理",drops:5},{oil:"圆柚",drops:5},{oil:"元气",drops:5},{oil:"新瑞活力",drops:5},{oil:"生姜",drops:5},{oil:"广藿香",drops:5}]},
|
||
{name:"三伏晒背",note:"",tags:[],ingredients:[{oil:"乳香",drops:5},{oil:"薰衣草",drops:5},{oil:"罗马洋甘菊",drops:5},{oil:"永久花",drops:5},{oil:"蓝艾菊",drops:3},{oil:"西洋蓍草",drops:10}]},
|
||
{name:"三伏百会",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"特瑞活力",drops:10},{oil:"古巴香脂",drops:10}]},
|
||
{name:"三伏八髎",note:"",tags:[],ingredients:[{oil:"生姜",drops:10},{oil:"乳香",drops:10},{oil:"印蒿",drops:3},{oil:"温柔呵护",drops:4},{oil:"完美修护",drops:6},{oil:"花样年华焕肤油",drops:4}]},
|
||
{name:"三伏大椎",note:"",tags:[],ingredients:[{oil:"斯里兰卡肉桂皮",drops:5},{oil:"檀香",drops:5},{oil:"西洋蓍草",drops:10},{oil:"生姜",drops:5},{oil:"乳香",drops:10},{oil:"广藿香",drops:10}]},
|
||
{name:"三伏檀中",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:2},{oil:"香蜂草",drops:30}]},
|
||
{name:"太伏太溪",note:"",tags:[],ingredients:[{oil:"元气",drops:15},{oil:"特瑞活力",drops:15}]},
|
||
{name:"三伏扶阳",note:"",tags:[],ingredients:[{oil:"乳香",drops:8},{oil:"广藿香",drops:3},{oil:"依兰依兰",drops:3},{oil:"生姜",drops:8},{oil:"檀香",drops:5},{oil:"斯里兰卡肉桂皮",drops:4},{oil:"当归",drops:2},{oil:"黑胡椒",drops:8},{oil:"柠檬草",drops:5},{oil:"杜松浆果",drops:8}]},
|
||
{name:"三阴交油",note:"",tags:[],ingredients:[{oil:"生姜",drops:6},{oil:"乳香",drops:10},{oil:"杜松浆果",drops:10},{oil:"快乐鼠尾草",drops:4}]},
|
||
{name:"八虚用油",note:"",tags:[],ingredients:[{oil:"元气",drops:5},{oil:"圆柚",drops:5},{oil:"新瑞活力",drops:5},{oil:"乳香",drops:5},{oil:"柠檬草",drops:5},{oil:"芳香调理",drops:5}]},
|
||
{name:"冬日小火炉",note:"",tags:[],ingredients:[{oil:"山鸡椒",drops:6},{oil:"生姜",drops:6},{oil:"黑胡椒",drops:6},{oil:"茉莉",drops:2},{oil:"柠檬草",drops:6},{oil:"天竺葵",drops:6},{oil:"温柔呵护",drops:10},{oil:"小茴香",drops:4}]},
|
||
{name:"女性内分泌",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:5},{oil:"乳香",drops:5},{oil:"依兰依兰",drops:5},{oil:"快乐鼠尾草",drops:5},{oil:"温柔呵护",drops:10},{oil:"温柔呵护",drops:5}]},
|
||
{name:"提升免疫力",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"茶树",drops:20},{oil:"保卫",drops:20}]},
|
||
{name:"面部精华",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:5},{oil:"丝柏",drops:5},{oil:"雪松",drops:5},{oil:"薰衣草",drops:5},{oil:"乳香",drops:5},{oil:"西洋蓍草",drops:10}]},
|
||
{name:"日常头疗",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"茶树",drops:5},{oil:"野橘",drops:5},{oil:"丝柏",drops:10},{oil:"生姜",drops:5},{oil:"雪松",drops:10},{oil:"薰衣草",drops:5},{oil:"乳香",drops:7},{oil:"安定情绪",drops:7}]},
|
||
{name:"肝肾保护",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:5},{oil:"生姜",drops:5},{oil:"杜松浆果",drops:5},{oil:"柠檬",drops:10},{oil:"元气",drops:5},{oil:"柠檬草",drops:5}]},
|
||
{name:"情绪能量油",note:"",tags:[],ingredients:[{oil:"乳香",drops:1},{oil:"柑橘绚烂",drops:10},{oil:"柑橘清新",drops:10},{oil:"安宁神气",drops:5},{oil:"安定情绪",drops:5}]},
|
||
{name:"养心宁神",note:"",tags:[],ingredients:[{oil:"依兰依兰",drops:5},{oil:"古巴香脂",drops:10},{oil:"乳香",drops:15}]},
|
||
{name:"安稳睡眠",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"薰衣草",drops:10},{oil:"安定情绪",drops:10}]},
|
||
{name:"过敏体质",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:20},{oil:"椒样薄荷",drops:10},{oil:"薰衣草",drops:10},{oil:"柠檬",drops:10}]},
|
||
{name:"淋巴结节",note:"",tags:[],ingredients:[{oil:"完美修护",drops:7},{oil:"丝柏",drops:5},{oil:"柠檬草",drops:7},{oil:"乳香",drops:10},{oil:"芳香调理",drops:5}]},
|
||
{name:"甲状腺结节",note:"",tags:[],ingredients:[{oil:"芳香调理",drops:5},{oil:"椒样薄荷",drops:5},{oil:"丁香花蕾",drops:5},{oil:"柠檬草",drops:5},{oil:"完美修护",drops:10},{oil:"乳香",drops:10}]},
|
||
{name:"皮肤过敏",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:8},{oil:"西洋蓍草",drops:10},{oil:"雪松",drops:5},{oil:"侧柏",drops:2},{oil:"茶树",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"止鼾安睡",note:"",tags:[],ingredients:[{oil:"安宁神气",drops:10},{oil:"罗勒",drops:5},{oil:"丝柏",drops:5},{oil:"道格拉斯冷杉",drops:5},{oil:"百里香",drops:5},{oil:"顺畅呼吸",drops:10}]},
|
||
{name:"哮喘缓解",note:"",tags:[],ingredients:[{oil:"顺畅呼吸",drops:10},{oil:"蓝艾菊",drops:5},{oil:"柠檬",drops:5},{oil:"薰衣草",drops:5},{oil:"椒样薄荷",drops:5},{oil:"赋活呼吸",drops:10}]},
|
||
{name:"带状疱疹",note:"",tags:[],ingredients:[{oil:"广藿香",drops:3},{oil:"佛手柑",drops:5},{oil:"天竺葵",drops:5},{oil:"丁香花蕾",drops:5},{oil:"百里香",drops:5},{oil:"香蜂草",drops:3},{oil:"完美修护",drops:7},{oil:"没药",drops:5},{oil:"乳香",drops:7}]},
|
||
{name:"夏日社痱",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:8},{oil:"椒样薄荷",drops:8},{oil:"薰衣草",drops:6},{oil:"广藿香",drops:8}]},
|
||
{name:"耳聋耳鸣",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:5},{oil:"香蜂草",drops:3},{oil:"天竺葵",drops:5},{oil:"罗勒",drops:10},{oil:"永久花",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"减脂瘦身",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:5},{oil:"乳香",drops:8},{oil:"杜松浆果",drops:10},{oil:"圆柚",drops:10},{oil:"迷迭香",drops:7},{oil:"丝柏",drops:15},{oil:"新瑞活力",drops:20}]},
|
||
{name:"痔疮用油",note:"",tags:[],ingredients:[{oil:"没药",drops:10},{oil:"乳香",drops:10},{oil:"天竺葵",drops:10},{oil:"永久花",drops:10},{oil:"丝柏",drops:15},{oil:"茶树",drops:20}]},
|
||
{name:"静脉曲张1",note:"",tags:[],ingredients:[{oil:"甜茴香",drops:7},{oil:"丝柏",drops:15},{oil:"薰衣草",drops:10},{oil:"柠檬草",drops:8},{oil:"永久花",drops:5},{oil:"芳香调理",drops:5}]},
|
||
{name:"静脉曲张2",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:3},{oil:"黑胡椒",drops:5},{oil:"永久花",drops:8},{oil:"丝柏",drops:10}]},
|
||
{name:"痛风缓解",note:"",tags:[],ingredients:[{oil:"柠檬草",drops:8},{oil:"西伯利亚冷杉",drops:5},{oil:"芳香调理",drops:10},{oil:"舒缓",drops:5},{oil:"西班牙牛至",drops:3},{oil:"乳香",drops:5},{oil:"香蜂草",drops:3}]},
|
||
{name:"滑膜炎症",note:"",tags:[],ingredients:[{oil:"丝柏",drops:5},{oil:"生姜",drops:5},{oil:"椒样薄荷",drops:5},{oil:"罗勒",drops:5},{oil:"冬青",drops:3},{oil:"乳香",drops:10},{oil:"道格拉斯冷杉",drops:5}]},
|
||
{name:"腱鞘炎症",note:"",tags:[],ingredients:[{oil:"罗勒",drops:5},{oil:"丝柏",drops:5},{oil:"乳香",drops:5},{oil:"薰衣草",drops:5},{oil:"圆柚",drops:5},{oil:"柠檬草",drops:10}]},
|
||
{name:"腰椎滑脱",note:"",tags:[],ingredients:[{oil:"丝柏",drops:5},{oil:"舒缓",drops:5},{oil:"椒样薄荷",drops:5},{oil:"檀香",drops:5},{oil:"冬青",drops:5},{oil:"马郁兰",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"暖宫调经1",note:"",tags:[],ingredients:[{oil:"温柔呵护",drops:10},{oil:"生姜",drops:10},{oil:"黑胡椒",drops:10},{oil:"天竺葵",drops:5},{oil:"温柔呵护",drops:25},{oil:"快乐鼠尾草",drops:15}]},
|
||
{name:"暖宫调经2",note:"",tags:[],ingredients:[{oil:"温柔呵护",drops:10},{oil:"生姜",drops:6},{oil:"黑胡椒",drops:6},{oil:"天竺葵",drops:3},{oil:"温柔呵护",drops:17},{oil:"快乐鼠尾草",drops:10}]},
|
||
{name:"经期止痛",note:"",tags:[],ingredients:[{oil:"黑胡椒",drops:5},{oil:"舒缓",drops:10},{oil:"生姜",drops:5},{oil:"乳香",drops:10},{oil:"温柔呵护",drops:10},{oil:"温柔呵护",drops:20},{oil:"薰衣草",drops:10}]},
|
||
{name:"乳腺增生",note:"",tags:[],ingredients:[{oil:"完美修护",drops:10},{oil:"薰衣草",drops:5},{oil:"迷迭香",drops:5},{oil:"百里香",drops:5},{oil:"丁香花蕾",drops:5},{oil:"柑橘清新",drops:10}]},
|
||
{name:"丰胸挺拨",note:"",tags:[],ingredients:[{oil:"小茴香",drops:20},{oil:"依兰依兰",drops:10},{oil:"温柔呵护",drops:30},{oil:"西洋蓍草",drops:30},{oil:"快乐鼠尾草",drops:10}]},
|
||
{name:"私密紧致",note:"",tags:[],ingredients:[{oil:"玫瑰呵护",drops:10},{oil:"茉莉呵护",drops:10},{oil:"丝柏",drops:15},{oil:"依兰依兰",drops:10},{oil:"快乐鼠尾草",drops:15}]},
|
||
{name:"乳腺结节1",note:"",tags:[],ingredients:[{oil:"完美修护",drops:10},{oil:"柑橘清新",drops:5},{oil:"没药",drops:10},{oil:"姜黄",drops:10},{oil:"丁香花蕾",drops:5},{oil:"乳香",drops:20}]},
|
||
{name:"乳腺结节2",note:"",tags:[],ingredients:[{oil:"丁香花蕾",drops:10},{oil:"乳香",drops:5},{oil:"柑橘清新",drops:10},{oil:"完美修护",drops:10},{oil:"柠檬草",drops:5}]},
|
||
{name:"手脚冰凉",note:"",tags:[],ingredients:[{oil:"生姜",drops:20},{oil:"天竺葵",drops:10},{oil:"岩兰草",drops:10},{oil:"柠檬草",drops:10},{oil:"黑胡椒",drops:15},{oil:"野橘",drops:10}]},
|
||
{name:"修复腹直肌",note:"",tags:[],ingredients:[{oil:"丝柏",drops:10},{oil:"乳香",drops:5},{oil:"永久花",drops:5},{oil:"圆柚",drops:10},{oil:"广藿香",drops:10},{oil:"柠檬草",drops:10}]},
|
||
{name:"子宫肌瘤1",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:5},{oil:"完美修护",drops:10},{oil:"安定情绪",drops:5},{oil:"西班牙牛至",drops:3},{oil:"姜黄",drops:10},{oil:"天竺葵",drops:5},{oil:"永久花",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"子宫肌瘤2",note:"",tags:[],ingredients:[{oil:"乳香",drops:5},{oil:"罗勒",drops:6},{oil:"天竺葵",drops:10}]},
|
||
{name:"卵巢囊肿1",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:8},{oil:"完美修护",drops:5},{oil:"温柔呵护",drops:50},{oil:"斯里兰卡肉桂皮",drops:5},{oil:"丝柏",drops:8},{oil:"迷迭香",drops:5},{oil:"安定情绪",drops:8},{oil:"乳香",drops:6}]},
|
||
{name:"卵巢囊肿2",note:"",tags:[],ingredients:[{oil:"乳香",drops:2},{oil:"温柔呵护",drops:2},{oil:"西班牙牛至",drops:1},{oil:"快乐鼠尾草",drops:1}]},
|
||
{name:"美背",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:10},{oil:"黑云杉",drops:15},{oil:"雪松",drops:10},{oil:"依兰依兰",drops:10},{oil:"佛手柑",drops:15},{oil:"圆柚",drops:10},{oil:"玫瑰呵护",drops:15},{oil:"当归",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"减轻妊娠纹",note:"",tags:[],ingredients:[{oil:"永久花",drops:7},{oil:"广藿香",drops:5},{oil:"没药",drops:4},{oil:"乳香",drops:7},{oil:"薰衣草",drops:7},{oil:"完美修护",drops:10},{oil:"花样年华焕肤油",drops:10}]},
|
||
{name:"更年期症",note:"",tags:[],ingredients:[{oil:"柠檬",drops:10},{oil:"椒样薄荷",drops:8},{oil:"薰衣草",drops:10},{oil:"天竺葵",drops:10},{oil:"温柔呵护",drops:30},{oil:"快乐鼠尾草",drops:20},{oil:"罗马洋甘菊",drops:12}]},
|
||
{name:"妇科炎症",note:"",tags:[],ingredients:[{oil:"没药",drops:10},{oil:"斯里兰卡肉桂皮",drops:10},{oil:"迷迭香",drops:20},{oil:"完美修护",drops:10},{oil:"杜松浆果",drops:10}]},
|
||
{name:"备孕调理",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:5},{oil:"生姜",drops:5},{oil:"温柔呵护",drops:10},{oil:"快乐鼠尾草",drops:5},{oil:"温柔呵护",drops:10}]},
|
||
{name:"抗衰紧致",note:"",tags:[],ingredients:[{oil:"丝柏",drops:5},{oil:"乳香",drops:5},{oil:"夏威夷檀香",drops:5},{oil:"玫瑰",drops:3},{oil:"永久花",drops:5},{oil:"西洋蓍草",drops:20}]},
|
||
{name:"美白淡斑1",note:"",tags:[],ingredients:[{oil:"玫瑰呵护",drops:5},{oil:"丁香花蕾",drops:2},{oil:"夏威夷檀香",drops:5},{oil:"扁柏",drops:5},{oil:"永久花",drops:5},{oil:"岩兰草",drops:5},{oil:"黑云杉",drops:5},{oil:"芹菜籽",drops:8},{oil:"花样年华焕肤油",drops:10}]},
|
||
{name:"美白淡斑2",note:"",tags:[],ingredients:[{oil:"广藿香",drops:3},{oil:"芹菜籽",drops:3},{oil:"黑云杉",drops:3},{oil:"西洋蓍草",drops:3},{oil:"橙花呵护",drops:4},{oil:"花样年华焕肤油",drops:3}]},
|
||
{name:"水油平衡",note:"",tags:[],ingredients:[{oil:"新清肌调理",drops:15},{oil:"天竺葵",drops:3},{oil:"雪松",drops:5},{oil:"椒样薄荷",drops:5},{oil:"永久花",drops:3},{oil:"麦卢卡",drops:3},{oil:"薰衣草",drops:5},{oil:"茶树",drops:5}]},
|
||
{name:"敏感肌",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"夏威夷檀香",drops:2},{oil:"岩兰草",drops:3},{oil:"广藿香",drops:5},{oil:"罗马洋甘菊",drops:10}]},
|
||
{name:"防晒修复",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:10},{oil:"乳香",drops:10},{oil:"椒样薄荷",drops:4},{oil:"永久花",drops:8},{oil:"柠檬草",drops:3},{oil:"薰衣草",drops:10},{oil:"夏威夷檀香",drops:5}]},
|
||
{name:"深层净化排毒",note:"",tags:[],ingredients:[{oil:"茶树呵护",drops:20},{oil:"杜松浆果",drops:10},{oil:"薰衣草",drops:20},{oil:"圆柚",drops:5},{oil:"蓝艾菊",drops:3},{oil:"丝柏",drops:3},{oil:"侧柏",drops:5},{oil:"雪松",drops:10}]},
|
||
{name:"蓝月光贵妇面霜",note:"",tags:[],ingredients:[{oil:"玫瑰呵护",drops:10},{oil:"芹菜籽",drops:2},{oil:"永久花",drops:2},{oil:"西洋蓍草",drops:10},{oil:"蓝艾菊",drops:5}]},
|
||
{name:"唇部保养",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:3},{oil:"没药",drops:3},{oil:"薰衣草",drops:5},{oil:"永久花",drops:3},{oil:"麦卢卡",drops:1},{oil:"乳香",drops:5}]},
|
||
{name:"皱纹推土机",note:"",tags:[],ingredients:[{oil:"花样年华焕肤油",drops:10},{oil:"桂花呵护",drops:6},{oil:"麦卢卡",drops:4},{oil:"永久花",drops:4},{oil:"黑云杉",drops:5},{oil:"夏威夷檀香",drops:5},{oil:"乳香",drops:10},{oil:"穗甘松",drops:6},{oil:"依兰依兰",drops:10}]},
|
||
{name:"祛除颈纹",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:5},{oil:"薰衣草",drops:5},{oil:"乳香",drops:10},{oil:"依兰依兰",drops:10},{oil:"穗甘松",drops:10}]},
|
||
{name:"蓝带神仙水",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"夏威夷檀香",drops:5},{oil:"蓝艾菊",drops:5},{oil:"永久花",drops:5},{oil:"西洋蓍草",drops:10}]},
|
||
{name:"全效紧致乳",note:"",tags:[],ingredients:[{oil:"花样年华焕肤油",drops:50},{oil:"橙花呵护",drops:50},{oil:"茉莉呵护",drops:50},{oil:"古巴香脂",drops:5},{oil:"西洋蓍草",drops:5},{oil:"雪松",drops:10},{oil:"玫瑰",drops:5}]},
|
||
{name:"眼袋黑眼圈",note:"",tags:[],ingredients:[{oil:"快乐鼠尾草",drops:5},{oil:"永久花",drops:2},{oil:"丝柏",drops:15},{oil:"乳香",drops:5},{oil:"西洋蓍草",drops:5}]},
|
||
{name:"清痘无痕",note:"",tags:[],ingredients:[{oil:"新清肌呵护",drops:20},{oil:"净化清新",drops:10},{oil:"薰衣草",drops:10},{oil:"花样年华焕肤油",drops:10},{oil:"茶树",drops:10}]},
|
||
{name:"脂肪粒",note:"",tags:[],ingredients:[{oil:"乳香",drops:5},{oil:"永久花",drops:3},{oil:"西班牙牛至",drops:3},{oil:"芫荽",drops:3},{oil:"雪松",drops:3},{oil:"茶树",drops:3},{oil:"罗马洋甘菊",drops:5}]},
|
||
{name:"植物蜂皮",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"橙花呵护",drops:10},{oil:"桂花呵护",drops:10},{oil:"永久花",drops:5},{oil:"没药",drops:3},{oil:"麦卢卡",drops:3},{oil:"花样年华焕肤油",drops:10}]},
|
||
{name:"植物水光针",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:10},{oil:"芹菜籽",drops:5},{oil:"玫瑰",drops:2},{oil:"乳香",drops:5},{oil:"木兰呵护",drops:20}]},
|
||
{name:"早C精华",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:20},{oil:"永久花",drops:5},{oil:"夏威夷檀香",drops:3},{oil:"穗甘松",drops:3},{oil:"乳香",drops:5},{oil:"玫瑰呵护",drops:10}]},
|
||
{name:"晚A精华",note:"",tags:[],ingredients:[{oil:"花样年华焕肤油",drops:10},{oil:"麦卢卡",drops:3},{oil:"芹菜籽",drops:5},{oil:"天竺葵",drops:5},{oil:"茉莉呵护",drops:10},{oil:"橙花呵护",drops:10},{oil:"雪松",drops:5}]},
|
||
{name:"消鸡皮肤",note:"",tags:[],ingredients:[{oil:"圆柚",drops:5},{oil:"薰衣草",drops:5},{oil:"枫香",drops:5},{oil:"柠檬香桃木",drops:5},{oil:"安定情绪",drops:5},{oil:"天竺葵",drops:6}]},
|
||
{name:"解荨麻疹",note:"",tags:[],ingredients:[{oil:"没药",drops:2},{oil:"乳香",drops:4},{oil:"椒样薄荷",drops:4},{oil:"罗马洋甘菊",drops:6},{oil:"薰衣草",drops:4},{oil:"蓝艾菊",drops:6}]},
|
||
{name:"保湿焕肤",note:"",tags:[],ingredients:[{oil:"永久花",drops:5},{oil:"没药",drops:5},{oil:"薰衣草",drops:5},{oil:"夏威夷檀香",drops:5},{oil:"玫瑰",drops:3},{oil:"天竺葵",drops:5},{oil:"玫瑰草",drops:5}]},
|
||
{name:"奶油桂花手霜",note:"",tags:[],ingredients:[{oil:"西洋蓍草",drops:10},{oil:"乳香",drops:10},{oil:"桂花",drops:8},{oil:"薰衣草",drops:8},{oil:"天竺葵",drops:8},{oil:"椰风香草",drops:8}]},
|
||
{name:"养护指甲",note:"",tags:[],ingredients:[{oil:"夏威夷檀香",drops:10},{oil:"罗马洋甘菊",drops:10},{oil:"没药",drops:10}]},
|
||
{name:"皮肤皲裂/脚后跟干裂",note:"",tags:[],ingredients:[{oil:"天竺葵",drops:10},{oil:"乳香",drops:10},{oil:"薰衣草",drops:8},{oil:"没药",drops:8}]},
|
||
{name:"黑绷带",note:"",tags:[],ingredients:[{oil:"广藿香",drops:6},{oil:"岩兰草",drops:6},{oil:"穗甘松",drops:6},{oil:"夏威夷檀香",drops:10},{oil:"永久花",drops:10},{oil:"没药",drops:10},{oil:"蓝艾菊",drops:10}]},
|
||
{name:"眉毛睫毛增长滋养液",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"迷迭香",drops:8},{oil:"雪松",drops:8},{oil:"依兰依兰",drops:6},{oil:"薰衣草",drops:8}]},
|
||
{name:"天鹅颈",note:"",tags:[],ingredients:[{oil:"穗甘松",drops:5},{oil:"柠檬草",drops:5},{oil:"完美修护",drops:10},{oil:"舒缓",drops:10},{oil:"依兰依兰",drops:10},{oil:"丝柏",drops:10},{oil:"芳香调理",drops:10},{oil:"乳香",drops:10}]},
|
||
{name:"收缩毛孔",note:"",tags:[],ingredients:[{oil:"迷迭香",drops:5},{oil:"依兰依兰",drops:7},{oil:"天竺葵",drops:10},{oil:"丝柏",drops:10}]},
|
||
{name:"酒糟鼻",note:"",tags:[],ingredients:[{oil:"乳香",drops:7},{oil:"薰衣草",drops:5},{oil:"岩兰草",drops:8},{oil:"罗马洋甘菊",drops:5},{oil:"茶树",drops:8}]},
|
||
{name:"疤痕修复",note:"",tags:[],ingredients:[{oil:"没药",drops:4},{oil:"乳香",drops:7},{oil:"薰衣草",drops:7},{oil:"永久花",drops:7},{oil:"花样年华焕肤油",drops:10}]},
|
||
{name:"太阳油",note:"",tags:[],ingredients:[{oil:"依兰依兰",drops:15},{oil:"广藿香",drops:15},{oil:"雪松",drops:15},{oil:"檀香",drops:15},{oil:"杜松浆果",drops:15}]},
|
||
{name:"月亮油",note:"",tags:[],ingredients:[{oil:"温柔呵护",drops:15},{oil:"茉莉呵护",drops:10},{oil:"玫瑰呵护",drops:10},{oil:"佛手柑",drops:5},{oil:"小茴香",drops:5},{oil:"依兰依兰",drops:7},{oil:"快乐鼠尾草",drops:10}]},
|
||
{name:"阿育吠陀",note:"",tags:[],ingredients:[{oil:"姜黄",drops:5},{oil:"生姜",drops:5},{oil:"杜松浆果",drops:10},{oil:"苦橙叶",drops:10},{oil:"迷迭香",drops:5},{oil:"柠檬",drops:10},{oil:"圆柚",drops:5}]},
|
||
{name:"缓解头痛",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"薰衣草",drops:5},{oil:"生姜",drops:5},{oil:"马郁兰",drops:7},{oil:"舒缓",drops:15}]},
|
||
{name:"白发变黑(女士)",note:"",tags:[],ingredients:[{oil:"乳香",drops:7},{oil:"快乐鼠尾草",drops:10},{oil:"依兰依兰",drops:5},{oil:"生姜",drops:7},{oil:"薰衣草",drops:5}]},
|
||
{name:"白发变黑(男士)",note:"",tags:[],ingredients:[{oil:"迷迭香",drops:10},{oil:"薰衣草",drops:10},{oil:"雪松",drops:10},{oil:"永久花",drops:5}]},
|
||
{name:"头屑清爽",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:10},{oil:"雪松",drops:20},{oil:"迷迭香",drops:10},{oil:"丝柏",drops:15},{oil:"茶树",drops:20}]},
|
||
{name:"口腔溃疡1",note:"",tags:[],ingredients:[{oil:"丁香花蕾",drops:5},{oil:"茶树",drops:10},{oil:"薰衣草",drops:5},{oil:"没药",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"口腔溃疡2",note:"",tags:[],ingredients:[{oil:"永久花",drops:5},{oil:"茶树",drops:10},{oil:"麦卢卡",drops:10},{oil:"月桂叶",drops:10},{oil:"佛手柑",drops:20},{oil:"薰衣草",drops:20}]},
|
||
{name:"扁桃腺炎",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"百里香",drops:5},{oil:"小豆蔻",drops:5},{oil:"尤加利",drops:5},{oil:"保卫",drops:5},{oil:"顺畅呼吸",drops:5}]},
|
||
{name:"慢性阑尾炎",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"柠檬草",drops:5},{oil:"西班牙牛至",drops:3},{oil:"保卫",drops:3},{oil:"乐活",drops:10},{oil:"新瑞活力",drops:5},{oil:"完美修护",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"结石、胆囊炎",note:"",tags:[],ingredients:[{oil:"罗勒",drops:5},{oil:"冬青",drops:5},{oil:"柠檬",drops:10},{oil:"天竺葵",drops:5},{oil:"迷迭香",drops:5},{oil:"圆柚",drops:10},{oil:"莱姆",drops:10},{oil:"椒样薄荷",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"脚气、灰指甲",note:"",tags:[],ingredients:[{oil:"香蜂草",drops:3},{oil:"保卫",drops:7},{oil:"茶树",drops:10},{oil:"西班牙牛至",drops:10},{oil:"广藿香",drops:5}]},
|
||
{name:"甲亢、甲减",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:9},{oil:"丁香花蕾",drops:12},{oil:"柠檬草",drops:15},{oil:"没药",drops:12},{oil:"乳香",drops:12}]},
|
||
{name:"降压仪式",note:"",tags:[],ingredients:[{oil:"依兰依兰",drops:10},{oil:"香蜂草",drops:8},{oil:"马郁兰",drops:15},{oil:"乳香",drops:15},{oil:"薰衣草",drops:12}]},
|
||
{name:"伤筋动骨",note:"",tags:[],ingredients:[{oil:"完美修护",drops:10},{oil:"乳香",drops:10},{oil:"生姜",drops:10},{oil:"西班牙牛至",drops:10},{oil:"冬青",drops:15},{oil:"柠檬草",drops:15},{oil:"舒缓",drops:5}]},
|
||
{name:"干眼症",note:"",tags:[],ingredients:[{oil:"薰衣草",drops:5},{oil:"乳香",drops:5},{oil:"罗马洋甘菊",drops:5},{oil:"快乐鼠尾草",drops:10},{oil:"古巴香脂",drops:5},{oil:"完美修护",drops:5}]},
|
||
{name:"儿童抚触",note:"",tags:[],ingredients:[{oil:"生姜",drops:3},{oil:"保卫",drops:3},{oil:"西伯利亚冷杉",drops:5},{oil:"乐活",drops:5},{oil:"没药",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"儿童脾胃",note:"",tags:[],ingredients:[{oil:"红橘",drops:10},{oil:"小茴香",drops:2},{oil:"生姜",drops:3},{oil:"乐活",drops:5}]},
|
||
{name:"视力养护",note:"",tags:[],ingredients:[{oil:"乳香",drops:3},{oil:"柠檬草",drops:3},{oil:"永久花",drops:2},{oil:"快乐鼠尾草",drops:5}]},
|
||
{name:"驱蚊喷雾",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"柠檬尤加利",drops:5},{oil:"尤加利",drops:5},{oil:"香茅",drops:5},{oil:"天竺葵",drops:3},{oil:"薰衣草",drops:10},{oil:"天然防护",drops:20}]},
|
||
{name:"个子高高",note:"",tags:[],ingredients:[{oil:"檀香",drops:3},{oil:"永久花",drops:3},{oil:"芳香调理",drops:2},{oil:"乳香",drops:5},{oil:"西伯利亚冷杉",drops:5}]},
|
||
{name:"清咽止咳",note:"",tags:[],ingredients:[{oil:"乳香",drops:5},{oil:"西伯利亚冷杉",drops:3},{oil:"尤加利",drops:3},{oil:"保卫",drops:2},{oil:"顺畅呼吸",drops:5}]},
|
||
{name:"通鼻消炎",note:"",tags:[],ingredients:[{oil:"茶树",drops:2},{oil:"椒样薄荷",drops:3},{oil:"乳香",drops:3},{oil:"蓝艾菊",drops:1},{oil:"尤加利",drops:3},{oil:"迷迭香",drops:2},{oil:"顺畅呼吸",drops:5}]},
|
||
{name:"蚊虫叮咬1",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"茶树",drops:3},{oil:"薰衣草",drops:3},{oil:"乳香",drops:2},{oil:"罗勒",drops:3},{oil:"天然防护",drops:5}]},
|
||
{name:"蚊虫叮咬2",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:5},{oil:"香茅",drops:5},{oil:"柠檬草",drops:5},{oil:"天然防护",drops:10}]},
|
||
{name:"学霸神助",note:"",tags:[],ingredients:[{oil:"檀香",drops:2},{oil:"完美修护",drops:2},{oil:"椒样薄荷",drops:4},{oil:"全神贯注",drops:8},{oil:"罗勒",drops:2},{oil:"迷迭香",drops:4},{oil:"乳香",drops:4}]},
|
||
{name:"磨牙安抚",note:"",tags:[],ingredients:[{oil:"佛手柑",drops:5},{oil:"罗勒",drops:5},{oil:"芳香调理",drops:5},{oil:"古巴香脂",drops:5},{oil:"安定情绪",drops:5}]},
|
||
{name:"免疫助力",note:"",tags:[],ingredients:[{oil:"野橘",drops:5},{oil:"茶树",drops:5},{oil:"侧柏",drops:3},{oil:"姜黄",drops:3},{oil:"保卫",drops:5},{oil:"乳香",drops:5}]},
|
||
{name:"腺样体肥大",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:3},{oil:"百里香",drops:2},{oil:"茶树",drops:3},{oil:"尤加利",drops:2},{oil:"雪松",drops:3},{oil:"顺畅呼吸",drops:5}]},
|
||
{name:"退烧方案",note:"",tags:[],ingredients:[{oil:"顺畅呼吸",drops:3},{oil:"保卫",drops:5},{oil:"椒样薄荷",drops:5},{oil:"茶树",drops:3},{oil:"薰衣草",drops:2},{oil:"乳香",drops:5}]},
|
||
{name:"手足口症",note:"",tags:[],ingredients:[{oil:"茶树",drops:3},{oil:"香蜂草",drops:3},{oil:"丁香花蕾",drops:3},{oil:"月桂叶",drops:3},{oil:"保卫",drops:3},{oil:"古巴香脂",drops:3}]},
|
||
{name:"湿疹修复",note:"",tags:[],ingredients:[{oil:"蓝艾菊",drops:2},{oil:"茶树",drops:2},{oil:"乳香",drops:3},{oil:"天竺葵",drops:3},{oil:"广藿香",drops:3},{oil:"罗马洋甘菊",drops:5}]},
|
||
{name:"抽动症",note:"",tags:[],ingredients:[{oil:"罗马洋甘菊",drops:3},{oil:"安定情绪",drops:5},{oil:"雪松",drops:3},{oil:"岩兰草",drops:3},{oil:"乳香",drops:5}]},
|
||
{name:"头疗生发",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:10},{oil:"茶树",drops:10},{oil:"迷迭香",drops:10},{oil:"丝柏",drops:20},{oil:"生姜",drops:10},{oil:"雪松",drops:20},{oil:"薰衣草",drops:10},{oil:"乳香",drops:14},{oil:"安定情绪",drops:14}]},
|
||
{name:"1、呼吸系统细胞律动(含香蜂草)",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"茶树",drops:4},{oil:"芳香调理",drops:4},{oil:"顺畅呼吸",drops:4},{oil:"迷迭香",drops:4},{oil:"尤加利",drops:4},{oil:"香蜂草",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"1、呼吸系统细胞律动(不含香蜂草)",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"茶树",drops:4},{oil:"芳香调理",drops:4},{oil:"顺畅呼吸",drops:4},{oil:"迷迭香",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"2、神经系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"百里香",drops:4},{oil:"丁香花蕾",drops:4},{oil:"芳香调理",drops:4},{oil:"柠檬草",drops:4},{oil:"香蜂草",drops:4},{oil:"广藿香",drops:4},{oil:"佛手柑",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"3、消化系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"百里香",drops:4},{oil:"芳香调理",drops:4},{oil:"佛手柑",drops:4},{oil:"芫荽",drops:4},{oil:"乐活",drops:4},{oil:"生姜",drops:4},{oil:"天竺葵",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"4、骨骼系统细胞律动(炎症控制)",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"茶树",drops:4},{oil:"冬青",drops:4},{oil:"芳香调理",drops:4},{oil:"柠檬草",drops:4},{oil:"西伯利亚冷杉",drops:4},{oil:"舒缓",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"5、淋巴系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"迷迭香",drops:4},{oil:"芳香调理",drops:4},{oil:"柠檬草",drops:4},{oil:"新瑞活力",drops:4},{oil:"元气",drops:4},{oil:"柠檬",drops:4},{oil:"圆柚",drops:4},{oil:"生姜",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"6、生殖系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"西班牙牛至",drops:4},{oil:"茶树",drops:4},{oil:"芳香调理",drops:4},{oil:"薰衣草",drops:4},{oil:"广藿香",drops:4},{oil:"芫荽",drops:4},{oil:"快乐鼠尾草",drops:4},{oil:"檀香",drops:4},{oil:"丁香花蕾",drops:4},{oil:"依兰依兰",drops:4},{oil:"天竺葵",drops:4},{oil:"温柔呵护",drops:4},{oil:"元气",drops:4},{oil:"温柔呵护",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"7、免疫系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"西班牙牛至",drops:4},{oil:"茶树",drops:4},{oil:"芳香调理",drops:4},{oil:"西伯利亚冷杉",drops:4},{oil:"丝柏",drops:4},{oil:"柠檬",drops:4},{oil:"莱姆",drops:4},{oil:"圆柚",drops:4},{oil:"保卫",drops:4},{oil:"丁香花蕾",drops:4},{oil:"百里香",drops:4},{oil:"元气",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"8、循环系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"百里香",drops:4},{oil:"芳香调理",drops:4},{oil:"柠檬草",drops:4},{oil:"保卫",drops:4},{oil:"马郁兰",drops:4},{oil:"罗勒",drops:4},{oil:"薰衣草",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"9、内分泌系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"快乐鼠尾草",drops:4},{oil:"芳香调理",drops:4},{oil:"天竺葵",drops:4},{oil:"保卫",drops:4},{oil:"依兰依兰",drops:4},{oil:"小茴香",drops:4},{oil:"薰衣草",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"10、感冒发烧系统细胞律动",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"西班牙牛至",drops:4},{oil:"百里香",drops:4},{oil:"保卫",drops:4},{oil:"芳香调理",drops:4},{oil:"柠檬草",drops:4},{oil:"尤加利",drops:4},{oil:"茶树",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"11、肌肉系统细胞律动(缓解疼痛)",note:"",tags:[],ingredients:[{oil:"乳香",drops:4},{oil:"生姜",drops:4},{oil:"芳香调理",drops:4},{oil:"冬青",drops:4},{oil:"柠檬草",drops:4},{oil:"西伯利亚冷杉",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"12、芳香调理技术",note:"",tags:[],ingredients:[{oil:"安定情绪",drops:4},{oil:"薰衣草",drops:4},{oil:"茶树",drops:4},{oil:"保卫",drops:4},{oil:"芳香调理",drops:4},{oil:"舒缓",drops:4},{oil:"野橘",drops:4},{oil:"椒样薄荷",drops:4}]},
|
||
{name:"芳心调理技术(单瓶购买)",note:"",tags:[],ingredients:[{oil:"静谧",drops:4},{oil:"抚慰",drops:4},{oil:"宽容",drops:4},{oil:"热情",drops:4},{oil:"欢欣",drops:4},{oil:"鼓舞",drops:4}]},
|
||
{name:"芳心调理技术(套装购买)",note:"",tags:[],ingredients:[{oil:"静谧",drops:4},{oil:"抚慰",drops:4},{oil:"宽容",drops:4},{oil:"热情",drops:4},{oil:"欢欣",drops:4},{oil:"鼓舞",drops:4}]},
|
||
{name:"头疼",note:"",tags:[],ingredients:[{oil:"尤加利",drops:1},{oil:"薰衣草",drops:1},{oil:"冬青",drops:2},{oil:"椒样薄荷",drops:2}]},
|
||
{name:"痤疮粉刺",note:"",tags:[],ingredients:[{oil:"茶树",drops:1}]},
|
||
{name:"植物热玛吉",note:"",tags:[],ingredients:[{oil:"玫瑰",drops:5},{oil:"丝柏",drops:10},{oil:"西洋蓍草",drops:20},{oil:"永久花",drops:5},{oil:"乳香",drops:10}]},
|
||
{name:"近视老花",note:"",tags:[],ingredients:[{oil:"乳香",drops:10},{oil:"快乐鼠尾草",drops:10},{oil:"花样年华焕肤油",drops:10}]},
|
||
{name:"记忆力",note:"",tags:[],ingredients:[{oil:"罗勒",drops:1},{oil:"迷迭香",drops:2},{oil:"佛手柑",drops:2}]},
|
||
{name:"皮肤老化",note:"",tags:[],ingredients:[{oil:"花样年华焕肤油",drops:3},{oil:"西洋蓍草",drops:3}]},
|
||
{name:"生发配方",note:"",tags:[],ingredients:[{oil:"生姜",drops:1},{oil:"迷迭香",drops:1},{oil:"乳香",drops:1},{oil:"雪松",drops:1},{oil:"薰衣草",drops:1},{oil:"扁柏",drops:1}]},
|
||
{name:"头皮屑",note:"",tags:[],ingredients:[{oil:"茶树",drops:1},{oil:"丝柏",drops:1},{oil:"迷迭香",drops:1}]},
|
||
{name:"生发2",note:"",tags:[],ingredients:[{oil:"生姜",drops:1},{oil:"薰衣草",drops:2},{oil:"迷迭香",drops:3},{oil:"雪松",drops:4}]},
|
||
{name:"白发转黑发",note:"",tags:[],ingredients:[{oil:"完美修护",drops:16},{oil:"乳香",drops:16},{oil:"雪松",drops:16},{oil:"薰衣草",drops:16},{oil:"丝柏",drops:16},{oil:"快乐鼠尾草",drops:16},{oil:"生姜",drops:16},{oil:"侧柏",drops:16},{oil:"扁柏",drops:16},{oil:"依兰依兰",drops:16},{oil:"迷迭香",drops:16},{oil:"檀香",drops:10},{oil:"西伯利亚冷杉",drops:10}]},
|
||
{name:"鼻炎用油",note:"",tags:[],ingredients:[{oil:"香蜂草",drops:4},{oil:"顺畅呼吸",drops:20},{oil:"椒样薄荷",drops:4},{oil:"罗勒",drops:6},{oil:"尤加利",drops:6}]},
|
||
{name:"中耳炎",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:1},{oil:"罗勒",drops:2},{oil:"茶树",drops:2},{oil:"薰衣草",drops:2}]},
|
||
{name:"口臭",note:"",tags:[],ingredients:[{oil:"绿薄荷",drops:1}]},
|
||
{name:"牙龈牙周",note:"",tags:[],ingredients:[{oil:"茶树",drops:1},{oil:"丁香花蕾",drops:1}]},
|
||
{name:"烧烫伤",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:2},{oil:"茶树",drops:2},{oil:"薰衣草",drops:2}]},
|
||
{name:"儿童长高",note:"",tags:[],ingredients:[{oil:"西伯利亚冷杉",drops:6},{oil:"乳香",drops:5},{oil:"檀香",drops:3},{oil:"永久花",drops:3},{oil:"芳香调理",drops:2}]},
|
||
{name:"湿疹",note:"",tags:[],ingredients:[{oil:"绿薄荷",drops:1},{oil:"侧柏",drops:5},{oil:"蓝艾菊",drops:5},{oil:"广藿香",drops:10},{oil:"乳香",drops:10}]},
|
||
{name:"退烧神器",note:"",tags:[],ingredients:[{oil:"杜松浆果",drops:3},{oil:"椒样薄荷",drops:4},{oil:"永久花",drops:3},{oil:"山鸡椒",drops:3},{oil:"保卫",drops:4},{oil:"柠檬",drops:3},{oil:"乳香",drops:4}]},
|
||
{name:"感冒咳嗽",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:1},{oil:"柠檬",drops:2},{oil:"乳香",drops:3},{oil:"西班牙牛至",drops:4},{oil:"保卫",drops:5}]},
|
||
{name:"腹泻",note:"",tags:[],ingredients:[{oil:"西班牙牛至",drops:1},{oil:"乐活",drops:1},{oil:"生姜",drops:1},{oil:"罗勒",drops:1}]},
|
||
{name:"晕车恶心",note:"",tags:[],ingredients:[{oil:"生姜",drops:1},{oil:"尤加利",drops:1},{oil:"佛手柑",drops:1},{oil:"椒样薄荷",drops:1},{oil:"乐活",drops:1}]},
|
||
{name:"醉酒",note:"",tags:[],ingredients:[{oil:"乐活",drops:2}]},
|
||
{name:"鸡眼/疣/痣",note:"",tags:[],ingredients:[{oil:"丁香花蕾",drops:1},{oil:"西班牙牛至",drops:1}]},
|
||
{name:"脚气",note:"",tags:[],ingredients:[{oil:"茶树",drops:1},{oil:"西班牙牛至",drops:1}]},
|
||
{name:"灰指甲2",note:"",tags:[],ingredients:[{oil:"茶树",drops:8},{oil:"西班牙牛至",drops:8},{oil:"百里香",drops:8}]},
|
||
{name:"丰胸挺拔",note:"",tags:[],ingredients:[{oil:"小茴香",drops:6},{oil:"依兰依兰",drops:4},{oil:"温柔呵护",drops:10},{oil:"西洋蓍草",drops:10},{oil:"快乐鼠尾草",drops:3}]},
|
||
{name:"痛经",note:"",tags:[],ingredients:[{oil:"斯里兰卡肉桂皮",drops:1},{oil:"玫瑰",drops:1},{oil:"丁香花蕾",drops:1},{oil:"薰衣草",drops:2}]},
|
||
{name:"亲密关系",note:"",tags:[],ingredients:[{oil:"快乐鼠尾草",drops:6},{oil:"茉莉呵护",drops:12},{oil:"檀香",drops:7},{oil:"依兰依兰",drops:6},{oil:"花样年华焕肤油",drops:5},{oil:"玫瑰呵护",drops:12}]},
|
||
{name:"前列腺养护",note:"",tags:[],ingredients:[{oil:"西班牙牛至",drops:2},{oil:"檀香",drops:3},{oil:"茉莉呵护",drops:4},{oil:"丝柏",drops:3},{oil:"乳香",drops:4},{oil:"永久花",drops:3},{oil:"迷迭香",drops:3},{oil:"快乐鼠尾草",drops:4}]},
|
||
{name:"手脚冰冷",note:"",tags:[],ingredients:[{oil:"生姜",drops:2},{oil:"圆柚",drops:2}]},
|
||
{name:"外阴瘙痒",note:"",tags:[],ingredients:[{oil:"茶树",drops:1},{oil:"没药",drops:1},{oil:"玫瑰草",drops:1},{oil:"佛手柑",drops:1}]},
|
||
{name:"淋巴排毒2",note:"",tags:[],ingredients:[{oil:"乳香",drops:1},{oil:"丝柏",drops:1},{oil:"迷迭香",drops:1},{oil:"柠檬草",drops:1},{oil:"永久花",drops:1},{oil:"新瑞活力",drops:1}]},
|
||
{name:"痔疮",note:"",tags:[],ingredients:[{oil:"没药",drops:1},{oil:"茶树",drops:1},{oil:"丝柏",drops:2},{oil:"永久花",drops:2}]},
|
||
{name:"便秘积食",note:"",tags:[],ingredients:[{oil:"乐活",drops:1},{oil:"柠檬",drops:1},{oil:"马郁兰",drops:1},{oil:"椒样薄荷",drops:1}]},
|
||
{name:"护肝排毒",note:"",tags:[],ingredients:[{oil:"当归",drops:1},{oil:"芫荽",drops:2},{oil:"元气",drops:3},{oil:"天竺葵",drops:3},{oil:"迷迭香",drops:7},{oil:"柠檬",drops:13}]},
|
||
{name:"痛风",note:"",tags:[],ingredients:[{oil:"百里香",drops:6},{oil:"杜松浆果",drops:8},{oil:"天竺葵",drops:10},{oil:"冬青",drops:10}]},
|
||
{name:"消除富贵包",note:"",tags:[],ingredients:[{oil:"柠檬草",drops:2},{oil:"乳香",drops:5},{oil:"芳香调理",drops:5},{oil:"生姜",drops:2},{oil:"舒缓",drops:3},{oil:"古巴香脂",drops:2}]},
|
||
{name:"焦虑",note:"",tags:[],ingredients:[{oil:"野橘",drops:1},{oil:"薰衣草",drops:2},{oil:"天竺葵",drops:2}]},
|
||
{name:"更年期",note:"",tags:[],ingredients:[{oil:"椒样薄荷",drops:2},{oil:"乳香",drops:3},{oil:"薰衣草",drops:3},{oil:"天竺葵",drops:4},{oil:"温柔呵护",drops:8},{oil:"快乐鼠尾草",drops:10},{oil:"罗马洋甘菊",drops:8}]},
|
||
{name:"失眠多梦",note:"",tags:[],ingredients:[{oil:"雪松",drops:1},{oil:"野橘",drops:1},{oil:"乳香",drops:1},{oil:"檀香",drops:1},{oil:"橙花",drops:1},{oil:"乐释",drops:1},{oil:"苦橙叶",drops:1},{oil:"薰衣草",drops:1},{oil:"马郁兰",drops:1},{oil:"佛手柑",drops:1},{oil:"岩兰草",drops:1},{oil:"罗马洋甘菊",drops:1}]},
|
||
{name:"稳定血糖",note:"",tags:[],ingredients:[{oil:"新瑞活力",drops:2},{oil:"斯里兰卡肉桂皮",drops:2},{oil:"芫荽",drops:2},{oil:"迷迭香",drops:2}]},
|
||
{name:"心脑血管护理",note:"",tags:[],ingredients:[{oil:"乳香",drops:2},{oil:"香蜂草",drops:1}]},
|
||
{name:"关节疼痛",note:"",tags:[],ingredients:[{oil:"乳香",drops:3},{oil:"舒缓",drops:2},{oil:"道格拉斯冷杉",drops:2},{oil:"柠檬草",drops:1}]},
|
||
{name:"高血压保健",note:"",tags:[],ingredients:[{oil:"依兰依兰",drops:10},{oil:"薰衣草",drops:10},{oil:"马郁兰",drops:10},{oil:"乳香",drops:10},{oil:"香蜂草",drops:5}]}
|
||
];
|
||
|
||
// ============ STATE ============
|
||
let recipes = [];
|
||
let currentRecipe = null;
|
||
let currentEditing = [];
|
||
|
||
function loadRecipes() {
|
||
try {
|
||
const saved = localStorage.getItem('doterra_recipes');
|
||
if (saved) {
|
||
recipes = JSON.parse(saved);
|
||
// Merge any new DEFAULT_RECIPES not yet in saved data
|
||
const existingNames = new Set(recipes.map(r => r.name));
|
||
let added = 0;
|
||
for (const dr of DEFAULT_RECIPES) {
|
||
if (!existingNames.has(dr.name)) {
|
||
recipes.push(JSON.parse(JSON.stringify(dr)));
|
||
added++;
|
||
}
|
||
}
|
||
if (added > 0) saveRecipes();
|
||
} else {
|
||
recipes = JSON.parse(JSON.stringify(DEFAULT_RECIPES));
|
||
saveRecipes();
|
||
}
|
||
} catch(e) {
|
||
recipes = JSON.parse(JSON.stringify(DEFAULT_RECIPES));
|
||
}
|
||
}
|
||
|
||
function saveRecipes() {
|
||
localStorage.setItem('doterra_recipes', JSON.stringify(recipes));
|
||
}
|
||
|
||
// ============ UTILS ============
|
||
function calcCost(ingredients) {
|
||
return ingredients.reduce((sum, ing) => {
|
||
const ppd = OILS[ing.oil] || 0;
|
||
return sum + ppd * ing.drops;
|
||
}, 0);
|
||
}
|
||
|
||
function fmtPrice(n) { return '¥ ' + n.toFixed(2); }
|
||
|
||
function showSection(name) {
|
||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
||
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
|
||
document.getElementById('section-' + name).classList.add('active');
|
||
const tabs = document.querySelectorAll('.nav-tab');
|
||
const map = {search:0, manage:1, add:2, oils:3};
|
||
tabs[map[name]].classList.add('active');
|
||
|
||
if (name === 'manage') { renderTagBar(); renderManage(); }
|
||
if (name === 'oils') renderOils();
|
||
if (name === 'add') renderNewIngList();
|
||
}
|
||
|
||
// ============ SEARCH SECTION ============
|
||
function filterRecipes() {
|
||
const q = document.getElementById('searchInput').value.trim().toLowerCase();
|
||
const filtered = q ? recipes.filter(r =>
|
||
r.name.toLowerCase().includes(q) ||
|
||
r.ingredients.some(i => i.oil.toLowerCase().includes(q)) ||
|
||
(r.tags || []).some(t => t.toLowerCase().includes(q))
|
||
) : recipes;
|
||
renderGrid(filtered);
|
||
}
|
||
|
||
function clearSearch() {
|
||
document.getElementById('searchInput').value = '';
|
||
filterRecipes();
|
||
document.getElementById('detailPanel').style.display = 'none';
|
||
}
|
||
|
||
function renderGrid(list) {
|
||
const grid = document.getElementById('recipeGrid');
|
||
if (!list.length) {
|
||
grid.innerHTML = '<div class="empty-state"><div class="empty-state-icon">🔍</div><div class="empty-state-text">没有找到相关配方</div></div>';
|
||
return;
|
||
}
|
||
grid.innerHTML = list.map((r, idx) => {
|
||
const realIdx = recipes.indexOf(r);
|
||
const cost = calcCost(r.ingredients);
|
||
const oilNames = r.ingredients.map(i => i.oil).join('、');
|
||
const tags = (r.tags || []).map(t => `<span class="tag">${t}</span>`).join(' ');
|
||
return `<div class="recipe-card" onclick="selectRecipe(${realIdx})">
|
||
<div class="recipe-card-name">${r.name}</div>
|
||
${tags ? '<div style="margin:4px 0">'+tags+'</div>' : ''}
|
||
<div class="recipe-card-oils">${oilNames}</div>
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px">
|
||
<div class="recipe-card-price" style="margin:0">💰 ${fmtPrice(cost)}</div>
|
||
<button class="btn btn-danger btn-sm" onclick="event.stopPropagation();deleteRecipeFromSearch(${realIdx})" style="padding:4px 10px;font-size:12px">🗑 删除</button>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function selectRecipe(idx) {
|
||
currentRecipe = idx;
|
||
currentEditing = JSON.parse(JSON.stringify(recipes[idx].ingredients));
|
||
document.getElementById('detailPanel').style.display = 'block';
|
||
renderDetail();
|
||
document.getElementById('detailPanel').scrollIntoView({behavior:'smooth', block:'start'});
|
||
}
|
||
|
||
function renderDetail() {
|
||
const r = recipes[currentRecipe];
|
||
document.getElementById('detailTitle').textContent = r.name;
|
||
const noteEl = document.getElementById('detailNote');
|
||
noteEl.innerHTML = r.note ? `<div class="detail-note">📝 ${r.note}</div>` : '';
|
||
|
||
const tbody = document.getElementById('ingredientsBody');
|
||
tbody.innerHTML = currentEditing.map((ing, i) => {
|
||
const ppd = OILS[ing.oil] || 0;
|
||
const sub = ppd * ing.drops;
|
||
const oilOptions = Object.keys(OILS).map(o =>
|
||
`<option value="${o}" ${o === ing.oil ? 'selected' : ''}>${o}</option>`
|
||
).join('');
|
||
return `<tr>
|
||
<td><select class="oil-select" onchange="changeOil(${i}, this.value)">${oilOptions}</select></td>
|
||
<td><input type="number" class="drops-input" value="${ing.drops}" min="0.5" step="0.5" onchange="changeDrops(${i}, this.value)"></td>
|
||
<td style="color:var(--text-light);font-size:13px">${ppd > 0 ? '¥'+ppd.toFixed(2) : '—'}</td>
|
||
<td style="font-weight:600;color:var(--sage-dark)">${sub > 0 ? fmtPrice(sub) : '—'}</td>
|
||
<td><button class="remove-btn" onclick="removeIng(${i})">×</button></td>
|
||
</tr>`;
|
||
}).join('');
|
||
|
||
const total = calcCost(currentEditing);
|
||
document.getElementById('totalPrice').textContent = fmtPrice(total);
|
||
|
||
renderCard();
|
||
hideAddRow();
|
||
}
|
||
|
||
function changeOil(i, val) {
|
||
currentEditing[i].oil = val;
|
||
renderDetail();
|
||
}
|
||
|
||
function changeDrops(i, val) {
|
||
currentEditing[i].drops = parseFloat(val) || 0;
|
||
renderDetail();
|
||
}
|
||
|
||
function removeIng(i) {
|
||
currentEditing.splice(i, 1);
|
||
renderDetail();
|
||
}
|
||
|
||
function addIngredientRow() {
|
||
const row = document.getElementById('addIngRow');
|
||
row.style.display = 'flex';
|
||
const sel = document.getElementById('newOilSelect');
|
||
if (sel.options.length <= 1) {
|
||
Object.keys(OILS).forEach(o => {
|
||
const opt = document.createElement('option');
|
||
opt.value = o; opt.textContent = o;
|
||
sel.appendChild(opt);
|
||
});
|
||
}
|
||
document.getElementById('newOilDrops').value = '';
|
||
}
|
||
|
||
function hideAddRow() { document.getElementById('addIngRow').style.display = 'none'; }
|
||
|
||
function confirmAddIngredient() {
|
||
const oil = document.getElementById('newOilSelect').value;
|
||
const drops = parseFloat(document.getElementById('newOilDrops').value);
|
||
if (!oil || !drops || drops <= 0) { alert('请选择精油并输入滴数'); return; }
|
||
currentEditing.push({oil, drops});
|
||
renderDetail();
|
||
}
|
||
|
||
function saveCurrentEditing() {
|
||
if (currentRecipe === null) return;
|
||
recipes[currentRecipe].ingredients = JSON.parse(JSON.stringify(currentEditing));
|
||
saveRecipes();
|
||
alert('✅ 配方已保存!');
|
||
}
|
||
|
||
// ============ CARD ============
|
||
function renderCard() {
|
||
const r = recipes[currentRecipe];
|
||
document.getElementById('cardTitle').textContent = r.name;
|
||
document.getElementById('cardNote').textContent = r.note || '';
|
||
document.getElementById('cardDate').textContent = '制作日期:' + new Date().toLocaleDateString('zh-CN');
|
||
|
||
const ul = document.getElementById('cardIngredients');
|
||
ul.innerHTML = currentEditing.map(ing => {
|
||
const ppd = OILS[ing.oil] || 0;
|
||
const sub = ppd * ing.drops;
|
||
return `<li>
|
||
<span class="card-oil-name">${ing.oil}</span>
|
||
<span class="card-oil-drops">${ing.drops} 滴</span>
|
||
<span class="card-oil-cost">${sub > 0 ? fmtPrice(sub) : ''}</span>
|
||
</li>`;
|
||
}).join('');
|
||
|
||
const total = calcCost(currentEditing);
|
||
document.getElementById('cardTotal').textContent = fmtPrice(total);
|
||
}
|
||
|
||
function exportCard() {
|
||
const el = document.getElementById('recipe-card-export');
|
||
html2canvas(el, {scale: 3, backgroundColor: null, useCORS: true}).then(canvas => {
|
||
const link = document.createElement('a');
|
||
link.download = recipes[currentRecipe].name + '_配方卡.png';
|
||
link.href = canvas.toDataURL('image/png');
|
||
link.click();
|
||
});
|
||
}
|
||
|
||
// ============ TAG SYSTEM ============
|
||
let allTags = [];
|
||
let manageFilterTag = null; // null = show all
|
||
let selectedRecipes = new Set();
|
||
|
||
function loadTags() {
|
||
try {
|
||
const saved = localStorage.getItem('doterra_tags');
|
||
if (saved) allTags = JSON.parse(saved);
|
||
else allTags = [];
|
||
} catch(e) { allTags = []; }
|
||
// Also collect tags from recipes
|
||
recipes.forEach(r => {
|
||
(r.tags || []).forEach(t => {
|
||
if (!allTags.includes(t)) allTags.push(t);
|
||
});
|
||
});
|
||
saveTags();
|
||
}
|
||
|
||
function saveTags() {
|
||
localStorage.setItem('doterra_tags', JSON.stringify(allTags));
|
||
}
|
||
|
||
function addGlobalTag() {
|
||
const input = document.getElementById('newTagInput');
|
||
const name = input.value.trim();
|
||
if (!name) return;
|
||
// Support multiple separated by comma
|
||
const newTags = name.split(/[,,、]/).map(s => s.trim()).filter(Boolean);
|
||
newTags.forEach(t => {
|
||
if (!allTags.includes(t)) allTags.push(t);
|
||
});
|
||
saveTags();
|
||
input.value = '';
|
||
renderTagBar();
|
||
}
|
||
|
||
function deleteGlobalTag(tag) {
|
||
if (!confirm(`删除标签「${tag}」?\n(已标记此标签的配方不会被删除,只是移除标签)`)) return;
|
||
allTags = allTags.filter(t => t !== tag);
|
||
// Remove from all recipes too
|
||
recipes.forEach(r => {
|
||
if (r.tags) r.tags = r.tags.filter(t => t !== tag);
|
||
});
|
||
saveTags();
|
||
saveRecipes();
|
||
renderTagBar();
|
||
renderManage();
|
||
}
|
||
|
||
function renderTagBar() {
|
||
const bar = document.getElementById('tagBar');
|
||
if (!allTags.length) {
|
||
bar.innerHTML = '<span style="font-size:12px;color:var(--text-light)">暂无标签,添加一个吧</span>';
|
||
return;
|
||
}
|
||
// "All" button
|
||
let html = `<div class="tag-btn ${manageFilterTag === null ? 'active' : ''}" onclick="filterByTag(null)">全部</div>`;
|
||
// "Untagged" button
|
||
html += `<div class="tag-btn ${manageFilterTag === '__other__' ? 'active' : ''}" onclick="filterByTag('__other__')">其他</div>`;
|
||
allTags.forEach(t => {
|
||
const count = recipes.filter(r => (r.tags || []).includes(t)).length;
|
||
html += `<div class="tag-btn ${manageFilterTag === t ? 'active' : ''}" onclick="filterByTag('${t.replace(/'/g, "\\'")}')">
|
||
${t} <span style="opacity:0.6;font-size:11px">${count}</span>
|
||
<button class="tag-del" onclick="event.stopPropagation();deleteGlobalTag('${t.replace(/'/g, "\\'")}')" title="删除标签">×</button>
|
||
</div>`;
|
||
});
|
||
bar.innerHTML = html;
|
||
}
|
||
|
||
function filterByTag(tag) {
|
||
manageFilterTag = tag;
|
||
selectedRecipes.clear();
|
||
renderTagBar();
|
||
renderManage();
|
||
}
|
||
|
||
// ============ MANAGE SECTION ============
|
||
function renderManage() {
|
||
const list = document.getElementById('manageList');
|
||
const label = document.getElementById('manageFilterLabel');
|
||
|
||
// Filter by tag
|
||
let filtered = recipes.map((r, i) => ({ r, i }));
|
||
if (manageFilterTag === '__other__') {
|
||
filtered = filtered.filter(({ r }) => !r.tags || r.tags.length === 0);
|
||
label.textContent = '— 其他';
|
||
} else if (manageFilterTag) {
|
||
filtered = filtered.filter(({ r }) => (r.tags || []).includes(manageFilterTag));
|
||
label.textContent = '— ' + manageFilterTag;
|
||
} else {
|
||
label.textContent = '';
|
||
}
|
||
|
||
if (!filtered.length) {
|
||
list.innerHTML = '<div class="empty-state"><div class="empty-state-icon">📋</div><div class="empty-state-text">暂无配方</div></div>';
|
||
return;
|
||
}
|
||
list.innerHTML = filtered.map(({ r, i }) => {
|
||
const cost = calcCost(r.ingredients);
|
||
const oilNames = r.ingredients.map(ing => ing.oil).join('、');
|
||
const tags = (r.tags || []).map(t => `<span class="tag">${t}</span>`).join(' ');
|
||
const checked = selectedRecipes.has(i) ? 'checked' : '';
|
||
return `<div class="manage-item">
|
||
<input type="checkbox" ${checked} onchange="toggleSelect(${i}, this.checked)" style="width:18px;height:18px;accent-color:var(--sage);cursor:pointer">
|
||
<div class="manage-item-left">
|
||
<div class="manage-item-name">${r.name} ${tags}</div>
|
||
<div class="manage-item-oils">${oilNames}</div>
|
||
<div style="margin-top:6px;font-size:13px;color:var(--sage-dark);font-weight:600">${fmtPrice(cost)}</div>
|
||
</div>
|
||
<div class="manage-item-actions">
|
||
<button class="btn btn-outline btn-sm" onclick="editTags(${i})">🏷 标签</button>
|
||
<button class="btn btn-outline btn-sm" onclick="editFromManage(${i})">✏️ 编辑</button>
|
||
<button class="btn btn-danger btn-sm" onclick="deleteRecipe(${i})">🗑 删除</button>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function toggleSelect(i, checked) {
|
||
if (checked) selectedRecipes.add(i);
|
||
else selectedRecipes.delete(i);
|
||
}
|
||
|
||
function toggleSelectAll() {
|
||
// Get currently visible recipe indices
|
||
let visible = recipes.map((r, i) => ({ r, i }));
|
||
if (manageFilterTag === '__other__') {
|
||
visible = visible.filter(({ r }) => !r.tags || r.tags.length === 0);
|
||
} else if (manageFilterTag) {
|
||
visible = visible.filter(({ r }) => (r.tags || []).includes(manageFilterTag));
|
||
}
|
||
const visibleIds = visible.map(({ i }) => i);
|
||
const allSelected = visibleIds.every(i => selectedRecipes.has(i));
|
||
if (allSelected) {
|
||
visibleIds.forEach(i => selectedRecipes.delete(i));
|
||
} else {
|
||
visibleIds.forEach(i => selectedRecipes.add(i));
|
||
}
|
||
renderManage();
|
||
}
|
||
|
||
function editTags(i) {
|
||
const r = recipes[i];
|
||
const currentTags = new Set(r.tags || []);
|
||
|
||
const overlay = document.createElement('div');
|
||
overlay.className = 'tag-picker';
|
||
overlay.innerHTML = `<div class="tag-picker-card">
|
||
<div class="tag-picker-title">🏷 为「${r.name}」选择标签</div>
|
||
<div class="tag-picker-tags" id="tagPickerTags"></div>
|
||
<div style="display:flex;gap:8px;margin-bottom:16px;align-items:center">
|
||
<input type="text" id="tagPickerNew" class="form-control" placeholder="新标签…" style="flex:1;font-size:13px;padding:8px 12px"
|
||
onkeydown="if(event.key==='Enter'){addTagFromPicker();event.preventDefault()}">
|
||
<button class="btn btn-outline btn-sm" onclick="addTagFromPicker()">添加</button>
|
||
</div>
|
||
<div style="display:flex;gap:10px;justify-content:flex-end">
|
||
<button class="btn btn-outline btn-sm" onclick="closeTagPicker()">取消</button>
|
||
<button class="btn btn-primary btn-sm" onclick="saveTagPicker(${i})">确认</button>
|
||
</div>
|
||
</div>`;
|
||
document.body.appendChild(overlay);
|
||
|
||
window._tagPickerSet = currentTags;
|
||
renderTagPicker();
|
||
}
|
||
|
||
function renderTagPicker() {
|
||
const container = document.getElementById('tagPickerTags');
|
||
if (!container) return;
|
||
const selected = window._tagPickerSet;
|
||
container.innerHTML = allTags.map(t =>
|
||
`<div class="tag-pick ${selected.has(t) ? 'selected' : ''}" onclick="toggleTagPick('${t.replace(/'/g, "\\'")}')">${t}</div>`
|
||
).join('');
|
||
}
|
||
|
||
function toggleTagPick(tag) {
|
||
const s = window._tagPickerSet;
|
||
if (s.has(tag)) s.delete(tag);
|
||
else s.add(tag);
|
||
renderTagPicker();
|
||
}
|
||
|
||
function addTagFromPicker() {
|
||
const input = document.getElementById('tagPickerNew');
|
||
const name = input.value.trim();
|
||
if (!name) return;
|
||
const newTags = name.split(/[,,、]/).map(s => s.trim()).filter(Boolean);
|
||
newTags.forEach(t => {
|
||
if (!allTags.includes(t)) allTags.push(t);
|
||
window._tagPickerSet.add(t);
|
||
});
|
||
saveTags();
|
||
input.value = '';
|
||
renderTagPicker();
|
||
renderTagBar();
|
||
}
|
||
|
||
function saveTagPicker(i) {
|
||
recipes[i].tags = [...window._tagPickerSet];
|
||
saveRecipes();
|
||
// Update allTags
|
||
recipes[i].tags.forEach(t => {
|
||
if (!allTags.includes(t)) allTags.push(t);
|
||
});
|
||
saveTags();
|
||
closeTagPicker();
|
||
renderTagBar();
|
||
renderManage();
|
||
}
|
||
|
||
function closeTagPicker() {
|
||
const overlay = document.querySelector('.tag-picker');
|
||
if (overlay) overlay.remove();
|
||
window._tagPickerSet = null;
|
||
}
|
||
|
||
function addRecipeToSheet(ws, r, isFirst, fontTitle, fontHeader, fontBody, fontTotal) {
|
||
if (!isFirst) ws.addRow([]); // blank row between recipes
|
||
|
||
// Title row - merge A:D and add background color
|
||
const titleRow = ws.addRow([r.name]);
|
||
const rowNum = titleRow.number;
|
||
ws.mergeCells(rowNum, 1, rowNum, 4);
|
||
titleRow.getCell(1).font = fontTitle;
|
||
titleRow.getCell(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFC8DDC9' } };
|
||
titleRow.getCell(1).alignment = { horizontal: 'center', vertical: 'middle' };
|
||
titleRow.height = 36;
|
||
|
||
const centerAlign = { horizontal: 'center', vertical: 'middle' };
|
||
|
||
// Note row
|
||
if (r.note) {
|
||
const noteRow = ws.addRow(['备注:' + r.note]);
|
||
noteRow.eachCell(c => { c.font = fontBody; c.alignment = centerAlign; });
|
||
}
|
||
|
||
// Header row
|
||
const headerRow = ws.addRow(['精油', '滴数', '单价/滴', '小计']);
|
||
headerRow.eachCell(c => { c.font = fontHeader; c.alignment = centerAlign; });
|
||
|
||
// Ingredient rows
|
||
let total = 0;
|
||
r.ingredients.forEach(ing => {
|
||
const ppd = OILS[ing.oil] || 0;
|
||
const sub = ppd * ing.drops;
|
||
total += sub;
|
||
const row = ws.addRow([ing.oil, ing.drops, '¥' + ppd.toFixed(2), '¥' + sub.toFixed(2)]);
|
||
row.eachCell(c => { c.font = fontBody; c.alignment = centerAlign; });
|
||
});
|
||
|
||
// Total row
|
||
const totalRow = ws.addRow(['合计', '', '', '¥' + total.toFixed(2)]);
|
||
totalRow.eachCell(c => { c.font = fontTotal; c.alignment = centerAlign; });
|
||
}
|
||
|
||
async function exportExcel() {
|
||
const indices = selectedRecipes.size > 0 ? [...selectedRecipes].sort((a,b) => a-b) : recipes.map((_, i) => i);
|
||
if (!indices.length) { alert('没有配方可导出'); return; }
|
||
|
||
const wb = new ExcelJS.Workbook();
|
||
const fontTitle = { size: 20 };
|
||
const fontHeader = { size: 14 };
|
||
const fontBody = { size: 12 };
|
||
const fontTotal = { size: 12, bold: true };
|
||
const colWidths = [{ width: 20 }, { width: 10 }, { width: 14 }, { width: 14 }];
|
||
|
||
// Group recipes by tag
|
||
const tagGroups = {}; // tag -> [recipe]
|
||
const untagged = [];
|
||
|
||
indices.forEach(idx => {
|
||
const r = recipes[idx];
|
||
const tags = r.tags && r.tags.length > 0 ? r.tags : null;
|
||
if (!tags) {
|
||
untagged.push(r);
|
||
} else {
|
||
tags.forEach(t => {
|
||
if (!tagGroups[t]) tagGroups[t] = [];
|
||
tagGroups[t].push(r);
|
||
});
|
||
}
|
||
});
|
||
|
||
// "全部" sheet always comes first
|
||
const wsAll = wb.addWorksheet('全部');
|
||
wsAll.columns = colWidths;
|
||
indices.forEach((idx, n) => {
|
||
addRecipeToSheet(wsAll, recipes[idx], n === 0, fontTitle, fontHeader, fontBody, fontTotal);
|
||
});
|
||
|
||
// Then one sheet per tag
|
||
const hasAnyTags = Object.keys(tagGroups).length > 0;
|
||
if (hasAnyTags) {
|
||
for (const [tag, recs] of Object.entries(tagGroups)) {
|
||
const sheetName = tag.slice(0, 31).replace(/[\\\/\*\?\[\]:]/g, '_');
|
||
const ws = wb.addWorksheet(sheetName);
|
||
ws.columns = colWidths;
|
||
recs.forEach((r, n) => {
|
||
addRecipeToSheet(ws, r, n === 0, fontTitle, fontHeader, fontBody, fontTotal);
|
||
});
|
||
}
|
||
// Untagged → "其他" sheet
|
||
if (untagged.length) {
|
||
const ws = wb.addWorksheet('其他');
|
||
ws.columns = colWidths;
|
||
untagged.forEach((r, n) => {
|
||
addRecipeToSheet(ws, r, n === 0, fontTitle, fontHeader, fontBody, fontTotal);
|
||
});
|
||
}
|
||
}
|
||
|
||
// Generate and download
|
||
const buf = await wb.xlsx.writeBuffer();
|
||
const blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
||
const url = URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = '精油配方导出_' + new Date().toLocaleDateString('zh-CN').replace(/\//g, '-') + '.xlsx';
|
||
a.click();
|
||
URL.revokeObjectURL(url);
|
||
}
|
||
|
||
function editFromManage(i) {
|
||
showSection('search');
|
||
document.getElementById('searchInput').value = recipes[i].name;
|
||
filterRecipes();
|
||
selectRecipe(i);
|
||
}
|
||
|
||
function deleteRecipe(i) {
|
||
if (!confirm(`确认删除配方「${recipes[i].name}」?`)) return;
|
||
recipes.splice(i, 1);
|
||
saveRecipes();
|
||
renderManage();
|
||
}
|
||
|
||
function deleteRecipeFromSearch(i) {
|
||
if (!confirm(`确认删除配方「${recipes[i].name}」?`)) return;
|
||
recipes.splice(i, 1);
|
||
saveRecipes();
|
||
// Hide detail panel if the deleted recipe was selected
|
||
if (currentRecipe === i) {
|
||
document.getElementById('detailPanel').style.display = 'none';
|
||
currentRecipe = null;
|
||
} else if (currentRecipe > i) {
|
||
currentRecipe--;
|
||
}
|
||
filterRecipes();
|
||
}
|
||
|
||
// ============ ADD SECTION ============
|
||
let newIngredients = [{oil:'', drops:1}];
|
||
|
||
function renderNewIngList() {
|
||
const list = document.getElementById('newIngList');
|
||
list.innerHTML = newIngredients.map((ing, i) => {
|
||
const oilOptions = Object.keys(OILS).map(o =>
|
||
`<option value="${o}" ${o === ing.oil ? 'selected' : ''}>${o}</option>`
|
||
).join('');
|
||
return `<div class="new-ing-row">
|
||
<select class="form-control" style="flex:1" onchange="newIngChange(${i},'oil',this.value)">
|
||
<option value="">— 选择精油 —</option>${oilOptions}
|
||
</select>
|
||
<input type="number" class="form-control" style="width:90px" value="${ing.drops}" min="0.5" step="0.5" placeholder="滴数" onchange="newIngChange(${i},'drops',this.value)">
|
||
<button class="remove-btn" onclick="removeNewIng(${i})">×</button>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function newIngChange(i, field, val) {
|
||
if (field === 'drops') newIngredients[i].drops = parseFloat(val) || 0;
|
||
else newIngredients[i].oil = val;
|
||
}
|
||
|
||
function addNewIngRow() {
|
||
newIngredients.push({oil:'', drops:1});
|
||
renderNewIngList();
|
||
}
|
||
|
||
function removeNewIng(i) {
|
||
if (newIngredients.length <= 1) return;
|
||
newIngredients.splice(i, 1);
|
||
renderNewIngList();
|
||
}
|
||
|
||
// Check if two ingredient lists are the same
|
||
function isSameIngredients(a, b) {
|
||
if (a.length !== b.length) return false;
|
||
const sortA = [...a].sort((x, y) => x.oil.localeCompare(y.oil));
|
||
const sortB = [...b].sort((x, y) => x.oil.localeCompare(y.oil));
|
||
return sortA.every((ing, i) => ing.oil === sortB[i].oil && ing.drops === sortB[i].drops);
|
||
}
|
||
|
||
// Check duplicate before saving. Returns true if save should proceed.
|
||
function checkDuplicateAndSave(name, ingredients, note) {
|
||
const existing = recipes.filter(r => r.name === name);
|
||
if (existing.length > 0) {
|
||
const same = existing.some(r => isSameIngredients(r.ingredients, ingredients));
|
||
if (same) {
|
||
alert(`配方「${name}」已存在且内容一致,无需重复保存。`);
|
||
return false;
|
||
} else {
|
||
// Different content, suggest new name
|
||
let newName = name;
|
||
let n = 2;
|
||
while (recipes.some(r => r.name === newName)) {
|
||
newName = name + n;
|
||
n++;
|
||
}
|
||
const ok = confirm(`已有同名配方「${name}」但内容不一致。\n\n是否保存为「${newName}」?`);
|
||
if (!ok) return false;
|
||
recipes.push({ name: newName, note: note || '', ingredients: JSON.parse(JSON.stringify(ingredients)) });
|
||
saveRecipes();
|
||
alert('✅ 配方「' + newName + '」已保存!');
|
||
return true;
|
||
}
|
||
}
|
||
// No duplicate
|
||
recipes.push({ name, note: note || '', ingredients: JSON.parse(JSON.stringify(ingredients)) });
|
||
saveRecipes();
|
||
alert('✅ 配方「' + name + '」已保存!');
|
||
return true;
|
||
}
|
||
|
||
function saveNewRecipe() {
|
||
const name = document.getElementById('newRecipeName').value.trim();
|
||
if (!name) { alert('请输入配方名称'); return; }
|
||
const ings = newIngredients.filter(i => i.oil && i.drops > 0);
|
||
if (!ings.length) { alert('请至少添加一种精油'); return; }
|
||
const note = document.getElementById('newRecipeNote').value.trim();
|
||
if (checkDuplicateAndSave(name, ings, note)) {
|
||
clearNewForm();
|
||
}
|
||
}
|
||
|
||
function clearNewForm() {
|
||
document.getElementById('newRecipeName').value = '';
|
||
document.getElementById('newRecipeNote').value = '';
|
||
newIngredients = [{oil:'', drops:1}];
|
||
renderNewIngList();
|
||
}
|
||
|
||
// ============ SMART PASTE ============
|
||
// Parse a text chunk into [(oilName, drops), ...] pairs
|
||
// Handles: "芳香调理8永久花10檀香10" → [("芳香调理",8), ("永久花",10), ("檀香",10)]
|
||
function parseOilChunk(text, oilNames) {
|
||
const results = []; // {name, drops, found}
|
||
const notFound = [];
|
||
|
||
// First try: split by numbers → [(text, number), ...]
|
||
const pairs = [];
|
||
const regex = /([^\d]+?)(\d+\.?\d*)/g;
|
||
let m;
|
||
let lastEnd = 0;
|
||
while ((m = regex.exec(text)) !== null) {
|
||
pairs.push({ name: m[1].trim(), drops: parseFloat(m[2]) });
|
||
lastEnd = regex.lastIndex;
|
||
}
|
||
// Remaining text after last number (oil with no drops)
|
||
const remainder = text.slice(lastEnd).trim();
|
||
|
||
if (pairs.length > 0) {
|
||
// Got number-delimited pairs
|
||
pairs.forEach(p => {
|
||
if (!p.name) return;
|
||
// The name might contain multiple oils concatenated: try greedy match from oil list
|
||
const parsed = greedyMatchOils(p.name, oilNames);
|
||
if (parsed.length === 0) {
|
||
// Whole chunk is one oil name
|
||
const oil = findOil(p.name, oilNames);
|
||
if (oil) results.push({ oil, drops: p.drops });
|
||
else notFound.push(p.name);
|
||
} else if (parsed.length === 1) {
|
||
results.push({ oil: parsed[0], drops: p.drops });
|
||
} else {
|
||
// Multiple oils found before this number - last one gets the drops, others get 1
|
||
parsed.forEach((oil, idx) => {
|
||
results.push({ oil, drops: idx === parsed.length - 1 ? p.drops : 1 });
|
||
});
|
||
}
|
||
});
|
||
if (remainder) {
|
||
const oil = findOil(remainder, oilNames);
|
||
if (oil) results.push({ oil, drops: 1 });
|
||
else notFound.push(remainder);
|
||
}
|
||
} else {
|
||
// No numbers at all, try to find oil names in the text
|
||
const oil = findOil(text, oilNames);
|
||
if (oil) results.push({ oil, drops: 1 });
|
||
else notFound.push(text);
|
||
}
|
||
|
||
return { results, notFound };
|
||
}
|
||
|
||
// Try to greedily match known oil names from a concatenated string
|
||
// "芳香调理永久花" → ["芳香调理", "永久花"]
|
||
function greedyMatchOils(text, oilNames) {
|
||
if (!text) return [];
|
||
// Sort oil names by length descending (prefer longer matches)
|
||
const sorted = [...oilNames].sort((a, b) => b.length - a.length);
|
||
const matched = [];
|
||
let pos = 0;
|
||
|
||
while (pos < text.length) {
|
||
let found = false;
|
||
// Try exact match at current position
|
||
for (const name of sorted) {
|
||
if (text.startsWith(name, pos)) {
|
||
matched.push(name);
|
||
pos += name.length;
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!found) {
|
||
// Try fuzzy match: collect chars until we find a match
|
||
let end = pos + 1;
|
||
let fuzzyFound = false;
|
||
while (end <= text.length) {
|
||
const segment = text.slice(pos, end);
|
||
const oil = findOil(segment, oilNames);
|
||
// Check if this segment closely matches an oil (not just substring)
|
||
if (oil && editDistance(segment, oil) <= Math.max(1, Math.floor(oil.length / 3))) {
|
||
matched.push(oil);
|
||
pos = end;
|
||
fuzzyFound = true;
|
||
break;
|
||
}
|
||
end++;
|
||
}
|
||
if (!fuzzyFound) {
|
||
// Skip one character and try again
|
||
pos++;
|
||
}
|
||
}
|
||
}
|
||
return matched;
|
||
}
|
||
|
||
function smartPaste() {
|
||
const raw = document.getElementById('smartPasteInput').value.trim();
|
||
if (!raw) { alert('请输入配方内容'); return; }
|
||
|
||
// Split by comma, Chinese comma, newline, tab, semicolons
|
||
const parts = raw.split(/[,,、;\n\t]+/).map(s => s.trim()).filter(Boolean);
|
||
if (parts.length < 1) { alert('请输入配方内容'); return; }
|
||
|
||
const oilNames = Object.keys(OILS);
|
||
// Sort by length desc for greedy matching
|
||
const oilsSorted = [...oilNames].sort((a, b) => b.length - a.length);
|
||
let recipeName = '';
|
||
let startIdx = 0;
|
||
|
||
// Check if first part is purely a recipe name (no numbers, not an oil name)
|
||
const firstHasNum = /\d/.test(parts[0]);
|
||
const firstIsOil = findOil(parts[0], oilNames);
|
||
if (!firstHasNum && !firstIsOil) {
|
||
recipeName = parts[0];
|
||
startIdx = 1;
|
||
} else {
|
||
// First part contains oils — find where the first oil name starts
|
||
// Everything before that is the recipe name
|
||
const text = parts[0];
|
||
let firstOilPos = text.length;
|
||
for (const name of oilsSorted) {
|
||
const idx = text.indexOf(name);
|
||
if (idx !== -1 && idx < firstOilPos) {
|
||
firstOilPos = idx;
|
||
}
|
||
}
|
||
if (firstOilPos > 0) {
|
||
recipeName = text.slice(0, firstOilPos).trim();
|
||
parts[0] = text.slice(firstOilPos);
|
||
}
|
||
if (!recipeName) recipeName = '未命名配方';
|
||
startIdx = 0;
|
||
}
|
||
|
||
const ingredients = [];
|
||
const notFound = [];
|
||
|
||
for (let i = startIdx; i < parts.length; i++) {
|
||
const part = parts[i].trim();
|
||
if (!part) continue;
|
||
const { results, notFound: nf } = parseOilChunk(part, oilNames);
|
||
results.forEach(r => ingredients.push(r));
|
||
nf.forEach(n => notFound.push(n));
|
||
}
|
||
|
||
if (!ingredients.length && notFound.length) {
|
||
alert('没有识别到精油:' + notFound.join('、'));
|
||
return;
|
||
}
|
||
if (!ingredients.length) {
|
||
alert('没有识别到任何精油');
|
||
return;
|
||
}
|
||
|
||
const totalEO = ingredients.reduce((s, ing) => s + ing.drops, 0);
|
||
const needScale = totalEO > 20;
|
||
|
||
let scaledIngredients = null;
|
||
if (needScale) {
|
||
// Preserve user's previous coconut/ratio choices if they exist
|
||
const { coconut, ratio } = getScaleParams();
|
||
scaledIngredients = scaleRecipe(ingredients, coconut, ratio);
|
||
}
|
||
|
||
// Store data, then render
|
||
window._smartPasteData = { name: recipeName, ingredients, scaledIngredients, notFound };
|
||
renderSmartPasteResult();
|
||
}
|
||
|
||
function renderSmartPasteResult() {
|
||
const data = window._smartPasteData;
|
||
if (!data) return;
|
||
const { name: recipeName, ingredients, scaledIngredients, notFound } = data;
|
||
|
||
// Get current values from inputs if they exist, otherwise defaults
|
||
const coconutInput = document.getElementById('scaleCoconut');
|
||
const ratioSelect = document.getElementById('scaleRatio');
|
||
const coconutVal = coconutInput ? (parseFloat(coconutInput.value) || 10) : 10;
|
||
const ratioVal = ratioSelect ? (parseInt(ratioSelect.value) || 8) : 8;
|
||
|
||
const resultDiv = document.getElementById('smartPasteResult');
|
||
let html = `<div style="background:var(--sage-mist);border-radius:12px;padding:16px;margin-top:8px">`;
|
||
|
||
// Original version
|
||
html += renderRecipePreview('原始配方', recipeName, ingredients, notFound);
|
||
|
||
// Scaled version
|
||
if (scaledIngredients) {
|
||
html += `<div style="border-top:1px dashed var(--border);margin:14px 0"></div>`;
|
||
html += `<div style="margin-bottom:8px;display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
||
<span style="font-size:13px;color:var(--text-mid)">🔄 换算单次用量,椰子油</span>
|
||
<input type="number" id="scaleCoconut" value="${coconutVal}" min="1" step="1" style="width:60px;padding:4px 8px;border:1.5px solid var(--border);border-radius:6px;font-size:13px;text-align:center" oninput="rescalePreview()">
|
||
<span style="font-size:13px;color:var(--text-mid)">滴,稀释比例 1:</span>
|
||
<select id="scaleRatio" style="padding:4px 8px;border:1.5px solid var(--border);border-radius:6px;font-size:13px" onchange="rescalePreview()">
|
||
<option value="5" ${ratioVal===5?'selected':''}>5(较浓)</option>
|
||
<option value="6" ${ratioVal===6?'selected':''}>6</option>
|
||
<option value="7" ${ratioVal===7?'selected':''}>7</option>
|
||
<option value="8" ${ratioVal===8?'selected':''}>8(推荐)</option>
|
||
<option value="9" ${ratioVal===9?'selected':''}>9</option>
|
||
<option value="10" ${ratioVal===10?'selected':''}>10(较淡)</option>
|
||
</select>
|
||
</div>`;
|
||
html += `<div id="scaledPreviewContent">` + renderRecipePreview('单次用量', recipeName, scaledIngredients) + `</div>`;
|
||
}
|
||
|
||
html += `<div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">`;
|
||
if (scaledIngredients) {
|
||
html += `<button class="btn btn-primary btn-sm" onclick="confirmSmartPaste('scaled')">✅ 保存单次用量</button>`;
|
||
html += `<button class="btn btn-outline btn-sm" onclick="confirmSmartPaste('original')">保存原始配方</button>`;
|
||
} else {
|
||
html += `<button class="btn btn-primary btn-sm" onclick="confirmSmartPaste('original')">✅ 确认保存</button>`;
|
||
}
|
||
html += `<button class="btn btn-outline btn-sm" onclick="document.getElementById('smartPasteResult').innerHTML=''">取消</button>`;
|
||
html += `</div></div>`;
|
||
resultDiv.innerHTML = html;
|
||
}
|
||
|
||
function renderRecipePreview(label, name, ingredients, notFound) {
|
||
let html = `<div style="font-weight:600;margin-bottom:8px">${label}:「${name}」</div>
|
||
<div style="font-size:13px;line-height:2">`;
|
||
ingredients.forEach(ing => {
|
||
const cost = (OILS[ing.oil] || 0) * ing.drops;
|
||
html += `<span class="tag">${ing.oil} ${ing.drops}滴 (¥${cost.toFixed(2)})</span> `;
|
||
});
|
||
const total = ingredients.reduce((s, ing) => s + (OILS[ing.oil] || 0) * ing.drops, 0);
|
||
html += `</div><div style="margin-top:6px;font-size:13px;font-weight:600;color:var(--sage-dark)">总成本:¥${total.toFixed(2)}</div>`;
|
||
if (notFound && notFound.length) {
|
||
html += `<div style="margin-top:6px;font-size:12px;color:#c0392b">⚠️ 未识别:${notFound.join('、')}(精油库中没有找到)</div>`;
|
||
}
|
||
return html;
|
||
}
|
||
|
||
function scaleRecipe(ingredients, coconutDrops, ratio) {
|
||
ratio = ratio || 8; // default EO:coconut = 1:8
|
||
const totalEO = ingredients.reduce((s, ing) => s + ing.drops, 0);
|
||
// Target total EO based on ratio: totalEO_scaled = coconutDrops / ratio
|
||
const targetEO = coconutDrops / ratio;
|
||
const factor = targetEO / totalEO;
|
||
|
||
let scaled = ingredients.map(ing => ({
|
||
oil: ing.oil,
|
||
drops: Math.round(ing.drops * factor * 2) / 2 // round to 0.5
|
||
}));
|
||
// Ensure minimum 0.5
|
||
scaled = scaled.map(ing => ({ ...ing, drops: Math.max(0.5, ing.drops) }));
|
||
// Add coconut oil
|
||
scaled.push({ oil: '椰子油', drops: coconutDrops });
|
||
return scaled;
|
||
}
|
||
|
||
function getScaleParams() {
|
||
const coconut = parseFloat(document.getElementById('scaleCoconut')?.value) || 10;
|
||
const ratio = parseInt(document.getElementById('scaleRatio')?.value) || 8;
|
||
return { coconut, ratio };
|
||
}
|
||
|
||
function rescalePreview() {
|
||
const data = window._smartPasteData;
|
||
if (!data) return;
|
||
const { coconut, ratio } = getScaleParams();
|
||
data.scaledIngredients = scaleRecipe(data.ingredients, coconut, ratio);
|
||
// Only update the scaled preview (keeps input focus)
|
||
const scaledDiv = document.getElementById('scaledPreviewContent');
|
||
if (scaledDiv) {
|
||
scaledDiv.innerHTML = renderRecipePreview('单次用量', data.name, data.scaledIngredients);
|
||
}
|
||
}
|
||
|
||
function confirmSmartPaste(type) {
|
||
const data = window._smartPasteData;
|
||
if (!data) return;
|
||
const ings = type === 'scaled' ? data.scaledIngredients : data.ingredients;
|
||
const note = type === 'scaled' ? '单次用量(已换算)' : '';
|
||
if (checkDuplicateAndSave(data.name, ings, note)) {
|
||
document.getElementById('smartPasteInput').value = '';
|
||
document.getElementById('smartPasteResult').innerHTML = '';
|
||
window._smartPasteData = null;
|
||
}
|
||
}
|
||
|
||
function editDistance(a, b) {
|
||
const m = a.length, n = b.length;
|
||
const dp = Array.from({length: m+1}, () => Array(n+1).fill(0));
|
||
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
||
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
||
for (let i = 1; i <= m; i++)
|
||
for (let j = 1; j <= n; j++)
|
||
dp[i][j] = Math.min(
|
||
dp[i-1][j] + 1,
|
||
dp[i][j-1] + 1,
|
||
dp[i-1][j-1] + (a[i-1] === b[j-1] ? 0 : 1)
|
||
);
|
||
return dp[m][n];
|
||
}
|
||
|
||
function findOil(input, oilNames) {
|
||
// Exact match first
|
||
if (OILS[input] !== undefined) return input;
|
||
|
||
// Substring match (prefer longest)
|
||
let best = null;
|
||
let bestLen = 0;
|
||
for (const name of oilNames) {
|
||
if (name === input) return name;
|
||
if (name.includes(input) || input.includes(name)) {
|
||
if (name.length > bestLen) {
|
||
best = name;
|
||
bestLen = name.length;
|
||
}
|
||
}
|
||
}
|
||
if (best) return best;
|
||
|
||
// Edit distance fuzzy match (for typos like 永久化→永久花)
|
||
let bestDist = Infinity;
|
||
let bestFuzzy = null;
|
||
for (const name of oilNames) {
|
||
const dist = editDistance(input, name);
|
||
// Allow up to 1 char difference for short names, 2 for longer
|
||
const maxDist = Math.max(1, Math.floor(name.length / 3));
|
||
if (dist <= maxDist && dist < bestDist) {
|
||
bestDist = dist;
|
||
bestFuzzy = name;
|
||
}
|
||
}
|
||
return bestFuzzy;
|
||
}
|
||
|
||
// ============ OILS SECTION ============
|
||
let editingOil = null;
|
||
|
||
function renderOils(filter='') {
|
||
document.getElementById('oilCount').textContent = Object.keys(OILS).length;
|
||
const grid = document.getElementById('oilsGrid');
|
||
const entries = Object.entries(OILS).filter(([name]) =>
|
||
!filter || name.includes(filter)
|
||
).sort((a, b) => a[0].localeCompare(b[0], 'zh'));
|
||
grid.innerHTML = entries.map(([name, ppd]) => {
|
||
const meta = OILS_META[name] || {};
|
||
if (editingOil === name) {
|
||
const dc = meta.dropCount || '';
|
||
const volMap = {46:'2.5', 93:'5', 186:'10', 280:'15', 2146:'115'};
|
||
const vol = volMap[dc] || 'custom';
|
||
return `<div class="oil-chip" style="flex-wrap:wrap">
|
||
<span class="oil-chip-name" style="width:100%;margin-bottom:6px">${name}</span>
|
||
<input type="number" class="oil-edit-input" id="editBottlePrice" value="${meta.bottlePrice || ''}" step="0.01" min="0" placeholder="瓶价"
|
||
onkeydown="if(event.key==='Enter')saveOilEdit('${name}')">
|
||
<select class="oil-edit-input" id="editVolume" onchange="onVolumeChange('edit')" style="width:110px">
|
||
<option value="2.5" ${vol==='2.5'?'selected':''}>2.5ml</option>
|
||
<option value="5" ${vol==='5'?'selected':''}>5ml</option>
|
||
<option value="10" ${vol==='10'?'selected':''}>10ml</option>
|
||
<option value="15" ${vol==='15'?'selected':''}>15ml</option>
|
||
<option value="115" ${vol==='115'?'selected':''}>115ml</option>
|
||
<option value="custom" ${vol==='custom'?'selected':''}>自定义</option>
|
||
</select>
|
||
<input type="number" class="oil-edit-input" id="editDropCount" value="${dc}" step="1" min="1" placeholder="滴数"
|
||
style="display:${vol==='custom'?'inline-block':'none'}"
|
||
onkeydown="if(event.key==='Enter')saveOilEdit('${name}')">
|
||
<div class="oil-chip-actions">
|
||
<button class="oil-chip-btn" onclick="saveOilEdit('${name}')" title="保存">✓</button>
|
||
<button class="oil-chip-btn" onclick="cancelOilEdit()" title="取消">✕</button>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
return `<div class="oil-chip">
|
||
<span class="oil-chip-name">${name}</span>
|
||
<span class="oil-chip-price">¥${ppd.toFixed(2)}/滴</span>
|
||
<div class="oil-chip-actions">
|
||
<button class="oil-chip-btn" onclick="startOilEdit('${name}')" title="编辑">✏️</button>
|
||
<button class="oil-chip-btn del" onclick="deleteOil('${name}')" title="删除">🗑</button>
|
||
</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function filterOils() {
|
||
renderOils(document.getElementById('oilSearchInput').value.trim());
|
||
}
|
||
|
||
const VOLUME_DROPS = {'2.5': 46, '5': 93, '10': 186, '15': 280, '115': 2146};
|
||
|
||
function onVolumeChange(prefix) {
|
||
const volSelect = document.getElementById(prefix === 'new' ? 'newOilVolume' : 'editVolume');
|
||
const dropInput = document.getElementById(prefix === 'new' ? 'newOilDropCount' : 'editDropCount');
|
||
const vol = volSelect.value;
|
||
if (VOLUME_DROPS[vol]) {
|
||
dropInput.value = VOLUME_DROPS[vol];
|
||
dropInput.style.display = 'none';
|
||
} else if (vol === 'custom') {
|
||
dropInput.value = '';
|
||
dropInput.style.display = 'inline-block';
|
||
dropInput.focus();
|
||
} else {
|
||
dropInput.value = '';
|
||
dropInput.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
function getDropCount(prefix) {
|
||
const volSelect = document.getElementById(prefix === 'new' ? 'newOilVolume' : 'editVolume');
|
||
const dropInput = document.getElementById(prefix === 'new' ? 'newOilDropCount' : 'editDropCount');
|
||
const vol = volSelect.value;
|
||
if (VOLUME_DROPS[vol]) return VOLUME_DROPS[vol];
|
||
return parseInt(dropInput.value);
|
||
}
|
||
|
||
function addNewOil() {
|
||
const nameInput = document.getElementById('newOilName');
|
||
const bottlePriceInput = document.getElementById('newOilBottlePrice');
|
||
const name = nameInput.value.trim();
|
||
const bottlePrice = parseFloat(bottlePriceInput.value);
|
||
const dropCount = getDropCount('new');
|
||
if (!name) { alert('请输入精油名称'); return; }
|
||
if (isNaN(bottlePrice) || bottlePrice < 0) { alert('请输入有效的瓶价'); return; }
|
||
if (isNaN(dropCount) || dropCount <= 0) { alert('请选择容量或输入滴数'); return; }
|
||
if (OILS[name] !== undefined) {
|
||
const meta = OILS_META[name];
|
||
if (!confirm(`「${name}」已存在(¥${meta.bottlePrice}/${meta.dropCount}滴),要更新吗?`)) return;
|
||
}
|
||
OILS[name] = bottlePrice / dropCount;
|
||
OILS_META[name] = { bottlePrice, dropCount };
|
||
saveOils();
|
||
nameInput.value = '';
|
||
bottlePriceInput.value = '';
|
||
document.getElementById('newOilVolume').value = '';
|
||
document.getElementById('newOilDropCount').value = '';
|
||
document.getElementById('newOilDropCount').style.display = 'none';
|
||
filterOils();
|
||
}
|
||
|
||
function startOilEdit(name) {
|
||
editingOil = name;
|
||
filterOils();
|
||
setTimeout(() => {
|
||
const input = document.getElementById('editOilPrice');
|
||
if (input) { input.focus(); input.select(); }
|
||
}, 50);
|
||
}
|
||
|
||
function saveOilEdit(name) {
|
||
const bottlePrice = parseFloat(document.getElementById('editBottlePrice').value);
|
||
const dropCount = getDropCount('edit');
|
||
if (isNaN(bottlePrice) || bottlePrice < 0) { alert('请输入有效的瓶价'); return; }
|
||
if (isNaN(dropCount) || dropCount <= 0) { alert('请选择容量或输入滴数'); return; }
|
||
OILS[name] = bottlePrice / dropCount;
|
||
OILS_META[name] = { bottlePrice, dropCount };
|
||
saveOils();
|
||
editingOil = null;
|
||
filterOils();
|
||
}
|
||
|
||
function cancelOilEdit() {
|
||
editingOil = null;
|
||
filterOils();
|
||
}
|
||
|
||
function deleteOil(name) {
|
||
if (!confirm(`确认删除「${name}」?\n\n注意:已有配方中如果用到此精油,成本将显示为 ¥0`)) return;
|
||
delete OILS[name];
|
||
delete OILS_META[name];
|
||
saveOils();
|
||
filterOils();
|
||
}
|
||
|
||
// ============ INIT ============
|
||
loadOils();
|
||
loadRecipes();
|
||
loadTags();
|
||
filterRecipes();
|
||
</script>
|
||
</body>
|
||
</html>
|