文章目录[隐藏]
实战教程:在WordPress网站中添加在线食谱管理与食材采购清单工具
引言:为什么网站需要实用小工具?
在当今互联网时代,网站的功能性已成为吸引和留住用户的关键因素。对于美食、生活类网站而言,提供实用工具不仅能增加用户粘性,还能显著提升用户体验。想象一下,当用户在你的美食博客上找到心仪的食谱后,能够直接保存到个人收藏夹,并一键生成食材采购清单,这将大大简化他们的烹饪准备过程。
本教程将详细指导您如何通过WordPress代码二次开发,为您的网站添加在线食谱管理与食材采购清单工具。我们将从零开始,逐步构建这两个实用功能,无需依赖昂贵的插件,完全自主控制功能与样式。
第一部分:准备工作与环境搭建
1.1 开发环境要求
在开始之前,请确保您的开发环境满足以下要求:
- WordPress 5.0或更高版本
- PHP 7.2或更高版本(建议7.4+)
- MySQL 5.6或更高版本
- 基本的HTML、CSS、JavaScript和PHP知识
- 代码编辑器(如VS Code、Sublime Text等)
- 本地开发环境(如XAMPP、MAMP或Local by Flywheel)
1.2 创建子主题
为了避免主题更新时丢失自定义代码,我们首先创建一个子主题:
- 在WordPress的
wp-content/themes/目录下创建新文件夹,命名为my-cooking-tools - 在该文件夹中创建
style.css文件,添加以下内容:
/*
Theme Name: My Cooking Tools
Theme URI: https://yourwebsite.com
Description: 子主题用于添加食谱管理功能
Author: Your Name
Author URI: https://yourwebsite.com
Template: your-parent-theme // 替换为您的父主题名称
Version: 1.0.0
*/
/* 导入父主题样式 */
@import url("../your-parent-theme/style.css");
- 创建
functions.php文件,添加以下基础代码:
<?php
// 子主题功能文件
// 添加父主题样式
add_action('wp_enqueue_scripts', 'my_cooking_tools_enqueue_styles');
function my_cooking_tools_enqueue_styles() {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
wp_enqueue_style('child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style'));
}
// 在这里添加自定义功能代码
?>
- 在WordPress后台启用这个子主题
第二部分:数据库设计与数据模型
2.1 创建自定义数据库表
我们需要创建两个自定义表来存储食谱和食材清单数据。在子主题的functions.php文件中添加以下代码:
// 创建自定义数据库表
register_activation_hook(__FILE__, 'cooking_tools_create_tables');
function cooking_tools_create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$recipes_table = $wpdb->prefix . 'user_recipes';
$ingredients_table = $wpdb->prefix . 'recipe_ingredients';
$shopping_lists_table = $wpdb->prefix . 'shopping_lists';
// 用户食谱表
$recipes_sql = "CREATE TABLE IF NOT EXISTS $recipes_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
recipe_title varchar(255) NOT NULL,
recipe_content longtext NOT NULL,
prep_time int(11),
cook_time int(11),
servings int(11),
difficulty varchar(50),
category varchar(100),
tags text,
featured_image varchar(255),
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id)
) $charset_collate;";
// 食谱食材表
$ingredients_sql = "CREATE TABLE IF NOT EXISTS $ingredients_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
recipe_id mediumint(9) NOT NULL,
ingredient_name varchar(255) NOT NULL,
quantity decimal(10,2),
unit varchar(50),
notes text,
sort_order int(11) DEFAULT 0,
PRIMARY KEY (id),
KEY recipe_id (recipe_id)
) $charset_collate;";
// 采购清单表
$shopping_lists_sql = "CREATE TABLE IF NOT EXISTS $shopping_lists_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_id bigint(20) NOT NULL,
list_name varchar(255) NOT NULL,
ingredients text NOT NULL,
status varchar(50) DEFAULT 'active',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY user_id (user_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($recipes_sql);
dbDelta($ingredients_sql);
dbDelta($shopping_lists_sql);
}
2.2 数据模型类
为了更好的代码组织,我们创建一个数据模型类来处理数据库操作:
// 食谱管理数据模型类
class CookingTools_Model {
private $wpdb;
private $recipes_table;
private $ingredients_table;
private $shopping_lists_table;
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->recipes_table = $wpdb->prefix . 'user_recipes';
$this->ingredients_table = $wpdb->prefix . 'recipe_ingredients';
$this->shopping_lists_table = $wpdb->prefix . 'shopping_lists';
}
// 获取用户食谱
public function get_user_recipes($user_id, $limit = 20, $offset = 0) {
$sql = $this->wpdb->prepare(
"SELECT * FROM {$this->recipes_table} WHERE user_id = %d ORDER BY created_at DESC LIMIT %d OFFSET %d",
$user_id, $limit, $offset
);
return $this->wpdb->get_results($sql);
}
// 获取单个食谱
public function get_recipe($recipe_id, $user_id = null) {
if ($user_id) {
$sql = $this->wpdb->prepare(
"SELECT * FROM {$this->recipes_table} WHERE id = %d AND user_id = %d",
$recipe_id, $user_id
);
} else {
$sql = $this->wpdb->prepare(
"SELECT * FROM {$this->recipes_table} WHERE id = %d",
$recipe_id
);
}
return $this->wpdb->get_row($sql);
}
// 保存食谱
public function save_recipe($data) {
$defaults = array(
'user_id' => get_current_user_id(),
'created_at' => current_time('mysql'),
'updated_at' => current_time('mysql')
);
$data = wp_parse_args($data, $defaults);
if (isset($data['id']) && $data['id'] > 0) {
// 更新现有食谱
$recipe_id = $data['id'];
unset($data['id']);
$this->wpdb->update($this->recipes_table, $data, array('id' => $recipe_id));
return $recipe_id;
} else {
// 插入新食谱
unset($data['id']);
$this->wpdb->insert($this->recipes_table, $data);
return $this->wpdb->insert_id;
}
}
// 获取食谱食材
public function get_recipe_ingredients($recipe_id) {
$sql = $this->wpdb->prepare(
"SELECT * FROM {$this->ingredients_table} WHERE recipe_id = %d ORDER BY sort_order ASC",
$recipe_id
);
return $this->wpdb->get_results($sql);
}
// 保存食谱食材
public function save_recipe_ingredients($recipe_id, $ingredients) {
// 先删除旧的食材
$this->wpdb->delete($this->ingredients_table, array('recipe_id' => $recipe_id));
// 插入新食材
foreach ($ingredients as $index => $ingredient) {
$ingredient_data = array(
'recipe_id' => $recipe_id,
'ingredient_name' => sanitize_text_field($ingredient['name']),
'quantity' => floatval($ingredient['quantity']),
'unit' => sanitize_text_field($ingredient['unit']),
'notes' => sanitize_text_field($ingredient['notes']),
'sort_order' => $index
);
$this->wpdb->insert($this->ingredients_table, $ingredient_data);
}
}
// 获取用户采购清单
public function get_user_shopping_lists($user_id) {
$sql = $this->wpdb->prepare(
"SELECT * FROM {$this->shopping_lists_table} WHERE user_id = %d ORDER BY created_at DESC",
$user_id
);
return $this->wpdb->get_results($sql);
}
// 保存采购清单
public function save_shopping_list($data) {
$defaults = array(
'user_id' => get_current_user_id(),
'status' => 'active',
'created_at' => current_time('mysql'),
'updated_at' => current_time('mysql')
);
$data = wp_parse_args($data, $defaults);
if (isset($data['id']) && $data['id'] > 0) {
// 更新现有清单
$list_id = $data['id'];
unset($data['id']);
$this->wpdb->update($this->shopping_lists_table, $data, array('id' => $list_id));
return $list_id;
} else {
// 插入新清单
unset($data['id']);
$this->wpdb->insert($this->shopping_lists_table, $data);
return $this->wpdb->insert_id;
}
}
// 删除食谱
public function delete_recipe($recipe_id, $user_id) {
// 先删除相关食材
$this->wpdb->delete($this->ingredients_table, array('recipe_id' => $recipe_id));
// 再删除食谱
return $this->wpdb->delete($this->recipes_table,
array('id' => $recipe_id, 'user_id' => $user_id)
);
}
}
第三部分:前端界面设计与开发
3.1 创建食谱管理页面模板
在子主题目录中创建recipe-manager.php文件:
<?php
/**
* Template Name: 食谱管理器
*/
get_header(); ?>
<div class="cooking-tools-container">
<div class="recipe-manager-wrapper">
<header class="recipe-manager-header">
<h1><?php the_title(); ?></h1>
<div class="user-actions">
<?php if (is_user_logged_in()): ?>
<button id="add-new-recipe" class="btn btn-primary">添加新食谱</button>
<a href="#shopping-lists" class="btn btn-secondary">我的采购清单</a>
<?php else: ?>
<p>请<a href="<?php echo wp_login_url(get_permalink()); ?>">登录</a>以使用食谱管理功能</p>
<?php endif; ?>
</div>
</header>
<?php if (is_user_logged_in()): ?>
<div class="recipe-manager-content">
<!-- 食谱列表区域 -->
<section id="recipes-list" class="recipes-section">
<h2>我的食谱</h2>
<div class="recipes-filter">
<input type="text" id="recipe-search" placeholder="搜索食谱..." class="search-input">
<select id="category-filter" class="filter-select">
<option value="">所有分类</option>
<option value="早餐">早餐</option>
<option value="午餐">午餐</option>
<option value="晚餐">晚餐</option>
<option value="甜点">甜点</option>
<option value="饮品">饮品</option>
</select>
</div>
<div class="recipes-grid" id="recipes-container">
<!-- 食谱将通过AJAX加载 -->
<div class="loading-spinner">加载中...</div>
</div>
<div class="pagination" id="recipes-pagination"></div>
</section>
<!-- 食谱编辑/添加区域 -->
<section id="recipe-editor" class="editor-section" style="display:none;">
<h2 id="editor-title">添加新食谱</h2>
<form id="recipe-form" class="recipe-form">
<input type="hidden" id="recipe-id" name="recipe_id" value="0">
<div class="form-group">
<label for="recipe-title">食谱名称 *</label>
<input type="text" id="recipe-title" name="recipe_title" required class="form-control">
</div>
<div class="form-row">
<div class="form-group">
<label for="prep-time">准备时间 (分钟)</label>
<input type="number" id="prep-time" name="prep_time" min="0" class="form-control">
</div>
<div class="form-group">
<label for="cook-time">烹饪时间 (分钟)</label>
<input type="number" id="cook-time" name="cook_time" min="0" class="form-control">
</div>
<div class="form-group">
<label for="servings">份量</label>
<input type="number" id="servings" name="servings" min="1" class="form-control">
</div>
</div>
<div class="form-group">
<label for="difficulty">难度</label>
<select id="difficulty" name="difficulty" class="form-control">
<option value="">选择难度</option>
<option value="简单">简单</option>
<option value="中等">中等</option>
<option value="困难">困难</option>
</select>
</div>
<div class="form-group">
<label for="category">分类</label>
<select id="category" name="category" class="form-control">
<option value="">选择分类</option>
<option value="早餐">早餐</option>
<option value="午餐">午餐</option>
<option value="晚餐">晚餐</option>
<option value="甜点">甜点</option>
<option value="饮品">饮品</option>
<option value="其他">其他</option>
</select>
</div>
<div class="form-group">
<label for="recipe-content">食谱步骤 *</label>
<textarea id="recipe-content" name="recipe_content" rows="8" required class="form-control"></textarea>
<small class="form-text">请详细描述烹饪步骤,每一步用换行分隔</small>
</div>
<!-- 食材管理部分 -->
<div class="form-group">
<label>食材清单</label>
<div class="ingredients-container" id="ingredients-container">
<div class="ingredient-row">
<input type="text" class="ingredient-name" placeholder="食材名称" name="ingredients[0][name]">
<input type="number" step="0.01" class="ingredient-quantity" placeholder="数量" name="ingredients[0][quantity]">
<input type="text" class="ingredient-unit" placeholder="单位 (克/个/汤匙等)" name="ingredients[0][unit]">
<input type="text" class="ingredient-notes" placeholder="备注" name="ingredients[0][notes]">
<button type="button" class="btn-remove-ingredient">×</button>
</div>
</div>
<button type="button" id="add-ingredient" class="btn btn-secondary">添加食材</button>
</div>
<div class="form-group">
<label for="tags">标签</label>
<input type="text" id="tags" name="tags" class="form-control" placeholder="用逗号分隔标签,如:中式,辣味,健康">
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存食谱</button>
<button type="button" id="cancel-edit" class="btn btn-secondary">取消</button>
<button type="button" id="generate-shopping-list" class="btn btn-success" style="display:none;">生成采购清单</button>
</div>
</form>
</section>
<!-- 采购清单区域 -->
<section id="shopping-lists" class="shopping-section" style="display:none;">
<h2>我的采购清单</h2>
<div class="shopping-lists-container" id="shopping-lists-container">
<!-- 采购清单将通过AJAX加载 -->
</div>
<div class="shopping-list-editor" id="shopping-list-editor" style="display:none;">
<h3 id="shopping-list-title">新建采购清单</h3>
<form id="shopping-list-form">
<input type="hidden" id="shopping-list-id" name="list_id" value="0">
<div class="form-group">
<label for="list-name">清单名称</label>
<input type="text" id="list-name" name="list_name" required class="form-control">
</div>
<div class="form-group">
<label>清单内容</label>
<div class="shopping-items-container" id="shopping-items-container">
<div class="shopping-item-row">
<input type="text" class="shopping-item-name" placeholder="物品名称" name="items[0][name]">
<input type="number" step="0.01" class="shopping-item-quantity" placeholder="数量" name="items[0][quantity]">
<input type="text" class="shopping-item-unit" placeholder="单位" name="items[0][unit]">
<div class="item-checkbox">
<input type="checkbox" class="shopping-item-checked" name="items[0][checked]">
<label>已购</label>
</div>
<button type="button" class="btn-remove-item">×</button>
</div>
</div>
<button type="button" id="add-shopping-item" class="btn btn-secondary">添加物品</button>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">保存清单</button>
<button type="button" id="cancel-shopping-edit" class="btn btn-secondary">取消</button>
</div>
</form>
</div>
</section>
</div>
<?php endif; ?>
</div>
</div>
<?php get_footer(); ?>
### 3.2 添加CSS样式
在子主题的`style.css`文件中添加以下样式:
/ 食谱管理工具样式 /
/ 容器样式 /
.cooking-tools-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.recipe-manager-wrapper {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
/ 头部样式 /
.recipe-manager-header {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
padding: 30px;
text-align: center;
}
.recipe-manager-header h1 {
margin: 0 0 20px 0;
font-size: 2.5em;
}
.user-actions {
display: flex;
justify-content: center;
gap: 15px;
flex-wrap: wrap;
}
/ 按钮样式 /
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
transform: translateY(-2px);
}
.btn-secondary {
background-color: #f0f0f0;
color: #333;
}
.btn-secondary:hover {
background-color: #e0e0e0;
}
.btn-success {
background-color: #2196F3;
color: white;
}
.btn-success:hover {
background-color: #0b7dda;
}
/ 内容区域 /
.recipe-manager-content {
padding: 30px;
}
/ 食谱列表样式 /
.recipes-section, .editor-section, .shopping-section {
margin-bottom: 40px;
}
.recipes-section h2, .editor-section h2, .shopping-section h2 {
color: #333;
border-bottom: 2px solid #f0f0f0;
padding-bottom: 10px;
margin-bottom: 20px;
}
/ 筛选区域 /
.recipes-filter {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.search-input, .filter-select, .form-control {
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
flex: 1;
min-width: 200px;
}
/ 食谱网格 /
.recipes-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.recipe-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
border: 1px solid #eee;
}
.recipe-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.15);
}
.recipe-image {
height: 180px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
display: flex;
align-items: center;
justify-content: center;
color: #666;
}
.recipe-info {
padding: 20px;
}
.recipe-title {
font-size: 1.3em;
margin: 0 0 10px 0;
color: #333;
}
.recipe-meta {
display: flex;
gap: 15px;
margin-bottom: 15px;
color: #666;
font-size: 0.9em;
}
.recipe-meta span {
display: flex;
align-items: center;
gap: 5px;
}
.recipe-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
/ 表单样式 /
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #333;
}
.form-row {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.form-row .form-group {
flex: 1;
min-width: 200px;
}
/ 食材管理 /
.ingredients-container, .shopping-items-container {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
margin-bottom: 15px;
max-height: 300px;
overflow-y: auto;
}
.ingredient-row, .shopping-item-row {
display: flex;
gap: 10px;
margin-bottom: 10px;
align-items: center;
flex-wrap: wrap;
}
.ingredient-row input, .shopping-item-row input {
flex: 1;
min-width: 150px;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.ingredient-name, .shopping-item-name {
min-width: 200px !important;
}
.btn-remove-ingredient, .btn-remove-item {
background: #ff6b6b;
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-remove-ingredient:hover, .btn-remove-item:hover {
background: #ff5252;
}
/ 采购清单样式 /
.shopping-lists-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
}
.shopping-list-card {
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 3px 10px rgba(0,0,0,0.1);
border-left: 4px solid #4CAF50;
}
.shopping-list-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.shopping-list-title {
font-size: 1.2em;
margin: 0;
color: #333;
}
.shopping-list-date {
color: #666;
font-size: 0.9em;
}
.shopping-items-list {
margin: 15px 0;
}
.shopping-item {
display: flex;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.shopping-item:last-child {
border-bottom: none;
}
.shopping-item.checked {
opacity: 0.6;
text-decoration: line-through;
}
.item-checkbox {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
}
/ 响应式设计 /
@media (max-width: 768px) {
.recipes-grid {
grid-template-columns: 1fr;
}
.shopping-lists-container {
grid-template-columns: 1fr;
}
.form-row {
flex-direction: column;
}
.ingredient-row, .shopping-item-row {
flex-direction: column;
align-items: stretch;
}
.ingredient-row input, .shopping-item-row input {
min-width: 100% !important;
}
}
/ 加载动画 /
.loading-spinner {
text-align: center;
padding: 40px;
color: #666;
font-size: 1.1em;
}
/ 分页样式 /
.pagination {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 30px;
}
.page-number {
padding: 8px 15px;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.page-number.active {
background-color: #4CAF50;
color: white;
border-color: #4CAF50;
}
.page-number:hover:not(.active) {
background-color: #f0f0f0;
}
## 第四部分:JavaScript功能实现
### 4.1 创建JavaScript文件
在子主题目录中创建`js/cooking-tools.js`文件:
// 食谱管理工具JavaScript功能
jQuery(document).ready(function($) {
// 初始化变量
let currentPage = 1;
let recipesPerPage = 9;
let currentRecipeId = 0;
let ingredientCounter = 0;
let shoppingItemCounter = 0;
// 初始化模型对象
const cookingToolsModel = {
// 获取食谱列表
getRecipes: function(page = 1, search = '', category = '') {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_get_recipes',
page: page,
search: search,
category: category,
per_page: recipesPerPage,
nonce: cookingToolsAjax.nonce
}
});
},
// 获取单个食谱
getRecipe: function(recipeId) {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_get_recipe',
recipe_id: recipeId,
nonce: cookingToolsAjax.nonce
}
});
},
// 保存食谱
saveRecipe: function(recipeData) {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_save_recipe',
recipe_data: recipeData,
nonce: cookingToolsAjax.nonce
}
});
},
// 删除食谱
deleteRecipe: function(recipeId) {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_delete_recipe',
recipe_id: recipeId,
nonce: cookingToolsAjax.nonce
}
});
},
// 获取采购清单
getShoppingLists: function() {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_get_shopping_lists',
nonce: cookingToolsAjax.nonce
}
});
},
// 保存采购清单
saveShoppingList: function(listData) {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_save_shopping_list',
list_data: listData,
nonce: cookingToolsAjax.nonce
}
});
},
// 删除采购清单
deleteShoppingList: function(listId) {
return $.ajax({
url: cookingToolsAjax.ajax_url,
type: 'POST',
data: {
action: 'cooking_tools_delete_shopping_list',
list_id: listId,
nonce: cookingToolsAjax.nonce
}
});
}
};
// 初始化UI组件
const cookingToolsUI = {
// 显示食谱列表
showRecipesList: function() {
$('#recipe-editor').hide();
$('#shopping-lists').hide();
$('#recipes-list').show();
$('#add-new-recipe').text('添加新食谱');
},
// 显示食谱编辑器
showRecipeEditor: function(recipeId = 0) {
$('#recipes-list').hide();
$('#shopping-lists').hide();
$('#recipe-editor').show();
if (recipeId === 0) {
$('#editor-title').text('添加新食谱');
$('#recipe-id').val(0);
$('#recipe-form')[0].reset();
$('#ingredients-container').html(this.createIngredientRow(0));
ingredientCounter = 1;
$('#generate-shopping-list').hide();
} else {
$('#editor-title').text('编辑食谱');
$('#generate-shopping-list').show();
}
$('#add-new-recipe').text('返回食谱列表');
},
// 显示采购清单
showShoppingLists: function() {
$('#recipes-list').hide();
$('#recipe-editor').hide();
$('#shopping-lists').show();
$('#add-new-recipe').text('返回食谱列表');
},
// 创建食材行
createIngredientRow: function(index) {
return `
<div class="ingredient-row">
<input type="text" class="ingredient-name" placeholder="食材名称" name="ingredients[${index}][name]">
<input type="number" step="0.01" class="ingredient-quantity" placeholder="数量" name="ingredients[${index}][quantity]">
<input type="text" class="ingredient-unit" placeholder="单位 (克/个/汤匙等)" name="ingredients[${index}][unit]">
<input type="text" class="ingredient-notes" placeholder="备注" name="ingredients[${index}][notes]">
<button type="button" class="btn-remove-ingredient">×</button>
</div>
`;
},
// 创建采购物品行
createShoppingItemRow: function(index) {
return `
<div class="shopping-item-row">
<input type="text" class="shopping-item-name" placeholder="物品名称" name="items[${index}][name]">
<input type="number" step="0.01" class="shopping-item-quantity" placeholder="数量" name="items[${index}][quantity]">
<input type="text" class="shopping-item-unit" placeholder="单位" name="items[${index}][unit]">
<div class="item-checkbox">
<input type="checkbox" class="shopping-item-checked" name="items[${index}][checked]">
<label>已购</label>
</div>
<button type="button" class="btn-remove-item">×</button>
</div>
`;
},
// 渲染食谱卡片
renderRecipeCard: function(recipe) {
const prepTime = recipe.prep_time ? `${recipe.prep_time}分钟` : '未设置';
const cookTime = recipe.cook_time ? `${recipe.cook_time}分钟` : '未设置';
const servings = recipe.servings ? `${recipe.servings}人份` : '未设置';
return `
<div class="recipe-card" data-recipe-id="${recipe.id}">
<div class="recipe-image">
${recipe.featured_image ?
`<img src="${recipe.featured_image}" alt="${recipe.recipe_title}" style="width:100%;height:100%;object-fit:cover;">` :
'<span>暂无图片</span>'
}
</div>
<div class="recipe-info">
<h3 class="recipe-title">${recipe.recipe_title}</h3>
<div class="recipe-meta">
<span title="准备时间">⏱️ ${prepTime}</span>
<span title="烹饪时间">🔥 ${cookTime}</span>
<span title="份量">👥 ${servings}</span>
</div>
<div class="recipe-category">
<span class="category-badge">${recipe.category || '未分类'}</span>
${recipe.difficulty ? `<span class="difficulty-badge">${recipe.difficulty}</span>` : ''}
</div>
<div class="recipe-actions">
<button class="btn btn-secondary btn-view-recipe" data-id="${recipe.id}">查看</button>
<button class="btn btn-primary btn-edit-recipe" data-id="${recipe.id}">编辑</button>
<button class="btn btn-danger btn-delete-recipe" data-id="${recipe.id}">删除</button>
</div>
</div>
</div>
