文章目录
-
- 在当今数据驱动的互联网时代,问卷调查已成为网站收集用户反馈、进行市场研究、评估客户满意度的重要工具。虽然WordPress生态中有许多现成的问卷调查插件,如WPForms、Gravity Forms等,但这些通用解决方案往往无法完全满足特定业务需求。自定义问卷调查工具能够提供更精准的数据收集、更符合品牌形象的用户界面以及更灵活的集成能力。 通过WordPress代码二次开发实现自定义问卷调查工具,不仅可以节省长期使用付费插件的成本,还能确保工具完全按照您的业务流程和需求定制。本指南将详细介绍如何从零开始,为WordPress网站开发一个功能完善的自定义在线问卷调查工具。
-
- 在开始开发之前,需要确保具备以下环境: 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境 代码编辑器:Visual Studio Code、PHPStorm或Sublime Text 版本控制:Git用于代码版本管理 浏览器开发者工具:用于调试前端代码
- 在编写代码之前,明确问卷调查工具的核心需求: 问卷创建与管理:后台界面用于创建、编辑和删除问卷 问题类型支持:单选题、多选题、文本题、评分题等 响应收集与存储:安全地存储用户提交的问卷数据 数据分析与导出:后台查看统计结果并导出数据 前端展示与交互:用户友好的问卷填写界面 权限控制:限制问卷访问权限(公开/私有)
- 设计合理的数据库结构是开发成功的关键。我们需要创建以下数据表: surveys表:存储问卷基本信息 id (主键) title (问卷标题) description (问卷描述) status (状态:草稿/发布/归档) created_at (创建时间) updated_at (更新时间) questions表:存储问题信息 id (主键) survey_id (关联问卷ID) question_text (问题文本) question_type (问题类型:单选/多选/文本等) options (选项,JSON格式存储) required (是否必答) sort_order (排序) responses表:存储用户回答 id (主键) survey_id (问卷ID) question_id (问题ID) answer (用户答案) user_id (用户ID,匿名则为空) submitted_at (提交时间) session_id (会话ID,用于匿名用户跟踪)
-
- 在WordPress的wp-content/plugins/目录下创建新文件夹custom-survey-tool,并创建以下基础文件: custom-survey-tool/ ├── custom-survey-tool.php # 主插件文件 ├── includes/ │ ├── class-database.php # 数据库处理类 │ ├── class-survey.php # 问卷核心类 │ ├── class-admin.php # 后台管理类 │ └── class-frontend.php # 前端展示类 ├── admin/ │ ├── css/ │ │ └── admin-style.css # 后台样式 │ └── js/ │ └── admin-script.js # 后台脚本 ├── public/ │ ├── css/ │ │ └── public-style.css # 前端样式 │ └── js/ │ └── public-script.js # 前端脚本 ├── templates/ # 模板文件 └── assets/ # 静态资源
- 编辑custom-survey-tool.php文件,添加插件基本信息: <?php /** * Plugin Name: 自定义问卷调查工具 * Plugin URI: https://yourwebsite.com/ * Description: 为WordPress网站创建自定义在线问卷调查工具 * Version: 1.0.0 * Author: 您的名称 * License: GPL v2 or later * Text Domain: custom-survey-tool */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('CST_VERSION', '1.0.0'); define('CST_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CST_PLUGIN_URL', plugin_dir_url(__FILE__)); // 自动加载类文件 spl_autoload_register(function ($class_name) { $prefix = 'CST_'; $base_dir = CST_PLUGIN_DIR . 'includes/'; $len = strlen($prefix); if (strncmp($prefix, $class_name, $len) !== 0) { return; } $relative_class = substr($class_name, $len); $file = $base_dir . 'class-' . strtolower(str_replace('_', '-', $relative_class)) . '.php'; if (file_exists($file)) { require_once $file; } }); // 初始化插件 function cst_init() { // 检查WordPress版本 if (version_compare(get_bloginfo('version'), '5.0', '<')) { wp_die(__('本插件需要WordPress 5.0或更高版本', 'custom-survey-tool')); } // 实例化核心类 $database = new CST_Database(); $survey = new CST_Survey(); $admin = new CST_Admin(); $frontend = new CST_Frontend(); // 激活/停用钩子 register_activation_hook(__FILE__, array($database, 'create_tables')); register_deactivation_hook(__FILE__, array($database, 'drop_tables')); } add_action('plugins_loaded', 'cst_init');
-
- 创建includes/class-database.php文件,处理数据库表的创建与维护: <?php class CST_Database { public function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_prefix = $wpdb->prefix . 'cst_'; // 创建问卷表 $surveys_table = $table_prefix . 'surveys'; $sql1 = "CREATE TABLE IF NOT EXISTS $surveys_table ( id int(11) NOT NULL AUTO_INCREMENT, title varchar(255) NOT NULL, description text, status varchar(20) DEFAULT 'draft', created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; // 创建问题表 $questions_table = $table_prefix . 'questions'; $sql2 = "CREATE TABLE IF NOT EXISTS $questions_table ( id int(11) NOT NULL AUTO_INCREMENT, survey_id int(11) NOT NULL, question_text text NOT NULL, question_type varchar(50) NOT NULL, options text, required tinyint(1) DEFAULT 0, sort_order int(11) DEFAULT 0, PRIMARY KEY (id), KEY survey_id (survey_id) ) $charset_collate;"; // 创建回答表 $responses_table = $table_prefix . 'responses'; $sql3 = "CREATE TABLE IF NOT EXISTS $responses_table ( id int(11) NOT NULL AUTO_INCREMENT, survey_id int(11) NOT NULL, question_id int(11) NOT NULL, answer text NOT NULL, user_id int(11) DEFAULT NULL, session_id varchar(100) DEFAULT NULL, submitted_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY survey_id (survey_id), KEY question_id (question_id), KEY user_id (user_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql1); dbDelta($sql2); dbDelta($sql3); // 添加默认数据(示例问卷) $this->add_sample_data(); } private function add_sample_data() { global $wpdb; $table_prefix = $wpdb->prefix . 'cst_'; // 检查是否已有数据 $count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_prefix}surveys"); if ($count == 0) { // 添加示例问卷 $wpdb->insert( $table_prefix . 'surveys', array( 'title' => '客户满意度调查', 'description' => '帮助我们改进服务的简短问卷', 'status' => 'published' ) ); $survey_id = $wpdb->insert_id; // 添加示例问题 $sample_questions = array( array( 'survey_id' => $survey_id, 'question_text' => '您如何评价我们的产品质量?', 'question_type' => 'rating', 'options' => json_encode(array('min' => 1, 'max' => 5, 'labels' => array('非常差', '差', '一般', '好', '非常好'))), 'required' => 1, 'sort_order' => 1 ), array( 'survey_id' => $survey_id, 'question_text' => '您是通过什么渠道了解到我们的?', 'question_type' => 'checkbox', 'options' => json_encode(array('搜索引擎', '社交媒体', '朋友推荐', '广告', '其他')), 'required' => 0, 'sort_order' => 2 ), array( 'survey_id' => $survey_id, 'question_text' => '您有什么改进建议?', 'question_type' => 'textarea', 'options' => json_encode(array('rows' => 4)), 'required' => 0, 'sort_order' => 3 ) ); foreach ($sample_questions as $question) { $wpdb->insert($table_prefix . 'questions', $question); } } } public function drop_tables() { global $wpdb; $tables = array( $wpdb->prefix . 'cst_surveys', $wpdb->prefix . 'cst_questions', $wpdb->prefix . 'cst_responses' ); foreach ($tables as $table) { $wpdb->query("DROP TABLE IF EXISTS $table"); } } }
- 创建includes/class-survey.php文件,实现问卷的核心业务逻辑: <?php class CST_Survey { private $db; public function __construct() { global $wpdb; $this->db = $wpdb; $this->table_prefix = $wpdb->prefix . 'cst_'; } // 获取所有问卷 public function get_all_surveys($status = null) { $where = ''; if ($status) { $where = $this->db->prepare("WHERE status = %s", $status); } return $this->db->get_results( "SELECT * FROM {$this->table_prefix}surveys $where ORDER BY created_at DESC" ); } // 获取单个问卷 public function get_survey($id) { return $this->db->get_row( $this->db->prepare( "SELECT * FROM {$this->table_prefix}surveys WHERE id = %d", $id ) ); } // 获取问卷的所有问题 public function get_survey_questions($survey_id) { return $this->db->get_results( $this->db->prepare( "SELECT * FROM {$this->table_prefix}questions WHERE survey_id = %d ORDER BY sort_order ASC", $survey_id ) ); } // 创建新问卷 public function create_survey($data) { $defaults = array( 'title' => '新问卷', 'description' => '', 'status' => 'draft' ); $data = wp_parse_args($data, $defaults); $this->db->insert( $this->table_prefix . 'surveys', $data ); return $this->db->insert_id; } // 更新问卷 public function update_survey($id, $data) { return $this->db->update( $this->table_prefix . 'surveys', $data, array('id' => $id) ); } // 删除问卷 public function delete_survey($id) { // 先删除相关问题 $this->db->delete( $this->table_prefix . 'questions', array('survey_id' => $id) ); // 再删除问卷 return $this->db->delete( $this->table_prefix . 'surveys', array('id' => $id) ); } // 添加问题到问卷 public function add_question($survey_id, $data) { $defaults = array( 'survey_id' => $survey_id, 'question_text' => '新问题', 'question_type' => 'text', 'options' => '', 'required' => 0, 'sort_order' => 0 ); $data = wp_parse_args($data, $defaults); $this->db->insert( $this->table_prefix . 'questions', $data ); return $this->db->insert_id; } // 提交问卷回答 public function submit_response($survey_id, $responses, $user_id = null) { $session_id = $this->generate_session_id(); foreach ($responses as $question_id => $answer) { // 处理多选答案 if (is_array($answer)) { $answer = json_encode($answer); } $this->db->insert( $this->table_prefix . 'responses', array( 'survey_id' => $survey_id, 'question_id' => $question_id, 'answer' => $answer, 'user_id' => $user_id, 'session_id' => $session_id ) ); } return true; } // 获取问卷统计 public function get_survey_stats($survey_id) { $questions = $this->get_survey_questions($survey_id); $stats = array(); foreach ($questions as $question) { $answers = $this->db->get_results( $this->db->prepare( "SELECT answer FROM {$this->table_prefix}responses WHERE survey_id = %d AND question_id = %d", $survey_id, $question->id ) ); $question_stats = array( 'question' => $question->question_text, 'type' => $question->question_type, 'total_responses' => count($answers), 'answers' => array() ); // 根据问题类型统计答案 if ($question->question_type === 'radio' || $question->question_type === 'checkbox') { $options = json_decode($question->options, true); $counts = array(); foreach ($options as $option) { $counts[$option] = 0; } foreach ($answers as $answer) { if ($question->question_type === 'checkbox') { $user_answers = json_decode($answer->answer, true); if (is_array($user_answers)) { foreach ($user_answers as $user_answer) { if (isset($counts[$user_answer])) { $counts[$user_answer]++; } } } } else { if (isset($counts[$answer->answer])) { $counts[$answer->answer]++; } } } $question_stats['answers'] = $counts; } elseif ($question->question_type === 'rating') { $ratings = array(); foreach ($answers as $answer) { $ratings[] = intval($answer->answer); } if (!empty($ratings)) { $question_stats['answers'] = array( 'average' => round(array_sum($ratings) / count($ratings), 2), 'min' => min($ratings), 'max' => max($ratings), 'distribution' => array_count_values($ratings) ); } } $stats[] = $question_stats; } return $stats; } private function generate_session_id() { return md5(uniqid(rand(), true) . $_SERVER['REMOTE_ADDR']); } }
-
- public function add_admin_menu() { // 主菜单 add_menu_page( '问卷调查工具', '问卷调查', 'manage_options', 'custom-survey-tool', array($this, 'surveys_list_page'), 'dashicons-feedback', 30 ); // 子菜单 add_submenu_page( 'custom-survey-tool', '所有问卷', '所有问卷', 'manage_options', 'custom-survey-tool', array($this, 'surveys_list_page') ); add_submenu_page( 'custom-survey-tool', '添加新问卷', '添加新问卷', 'manage_options', 'cst-add-survey', array($this, 'add_survey_page') ); add_submenu_page( 'custom-survey-tool', '问卷统计', '问卷统计', 'manage_options', 'cst-survey-stats', array($this, 'survey_stats_page') ); add_submenu_page( 'custom-survey-tool', '插件设置', '设置', 'manage_options', 'cst-settings', array($this, 'settings_page') ); } public function enqueue_admin_scripts($hook) { // 只在插件页面加载资源 if (strpos($hook, 'custom-survey-tool') === false && strpos($hook, 'cst-') === false) { return; } wp_enqueue_style( 'cst-admin-style', CST_PLUGIN_URL . 'admin/css/admin-style.css', array(), CST_VERSION ); wp_enqueue_script( 'cst-admin-script', CST_PLUGIN_URL . 'admin/js/admin-script.js', array('jquery', 'jquery-ui-sortable'), CST_VERSION, true ); // 本地化脚本 wp_localize_script('cst-admin-script', 'cst_admin', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('cst_admin_nonce'), 'confirm_delete' => __('确定要删除这个问卷吗?此操作不可撤销。', 'custom-survey-tool') )); }
- public function surveys_list_page() { $action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : 'list'; $survey_id = isset($_GET['survey_id']) ? intval($_GET['survey_id']) : 0; switch ($action) { case 'edit': $this->edit_survey_page($survey_id); break; case 'view': $this->view_responses_page($survey_id); break; default: $this->display_surveys_list(); } } private function display_surveys_list() { $surveys = $this->survey->get_all_surveys(); ?> <div class="wrap cst-admin"> <h1 class="wp-heading-inline">问卷调查</h1> <a href="<?php echo admin_url('admin.php?page=cst-add-survey'); ?>" class="page-title-action"> 添加新问卷 </a> <?php if (isset($_GET['message'])): ?> <div class="notice notice-<?php echo sanitize_text_field($_GET['type'] ?? 'success'); ?> is-dismissible"> <p><?php echo esc_html($_GET['message']); ?></p> </div> <?php endif; ?> <div class="tablenav top"> <div class="alignleft actions"> <select name="status_filter" id="status_filter"> <option value="">所有状态</option> <option value="draft">草稿</option> <option value="published">已发布</option> <option value="archived">已归档</option> </select> <button class="button action" id="filter_action">筛选</button> </div> <br class="clear"> </div> <table class="wp-list-table widefat fixed striped surveys"> <thead> <tr> <th scope="col" class="column-id">ID</th> <th scope="col" class="column-title">问卷标题</th> <th scope="col" class="column-status">状态</th> <th scope="col" class="column-questions">问题数</th> <th scope="col" class="column-responses">回答数</th> <th scope="col" class="column-date">创建时间</th> <th scope="col" class="column-actions">操作</th> </tr> </thead> <tbody> <?php if (empty($surveys)): ?> <tr> <td colspan="7" class="no-items">暂无问卷,请点击"添加新问卷"创建第一个问卷。</td> </tr> <?php else: ?> <?php foreach ($surveys as $survey): $questions_count = $this->survey->get_questions_count($survey->id); $responses_count = $this->survey->get_responses_count($survey->id); ?> <tr> <td class="column-id"><?php echo $survey->id; ?></td> <td class="column-title"> <strong> <a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>"> <?php echo esc_html($survey->title); ?> </a> </strong> <div class="row-actions"> <span class="edit"> <a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>">编辑</a> | </span> <span class="view"> <a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>">统计</a> | </span> <span class="duplicate"> <a href="#" class="duplicate-survey" data-id="<?php echo $survey->id; ?>">复制</a> | </span> <span class="trash"> <a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=cst_delete_survey&survey_id=' . $survey->id), 'cst_delete_survey'); ?>" class="submitdelete" onclick="return confirm('确定要删除这个问卷吗?')">删除</a> </span> </div> </td> <td class="column-status"> <span class="status-badge status-<?php echo $survey->status; ?>"> <?php $status_labels = array( 'draft' => '草稿', 'published' => '已发布', 'archived' => '已归档' ); echo $status_labels[$survey->status] ?? $survey->status; ?> </span> </td> <td class="column-questions"><?php echo $questions_count; ?></td> <td class="column-responses"><?php echo $responses_count; ?></td> <td class="column-date"><?php echo date('Y-m-d H:i', strtotime($survey->created_at)); ?></td> <td class="column-actions"> <a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>" class="button button-small">编辑</a> <a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>" class="button button-small">查看统计</a> </td> </tr> <?php endforeach; ?> <?php endif; ?> </tbody> </table> </div> <?php }
- private function edit_survey_page($survey_id) { $survey = $this->survey->get_survey($survey_id); $questions = $this->survey->get_survey_questions($survey_id); if (!$survey) { wp_die('问卷不存在'); } ?> <div class="wrap cst-admin"> <h1>编辑问卷: <?php echo esc_html($survey->title); ?></h1> <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" id="survey-form"> <input type="hidden" name="action" value="cst_save_survey"> <input type="hidden" name="survey_id" value="<?php echo $survey_id; ?>"> <?php wp_nonce_field('cst_save_survey', 'cst_nonce'); ?> <div id="poststuff"> <div id="post-body" class="metabox-holder columns-2"> <!-- 主内容区 --> <div id="post-body-content"> <div class="postbox"> <h2 class="hndle">问卷基本信息</h2> <div class="inside"> <table class="form-table"> <tr> <th><label for="survey_title">问卷标题</label></th> <td> <input type="text" id="survey_title" name="survey_title" value="<?php echo esc_attr($survey->title); ?>" class="regular-text" required> </td> </tr> <tr> <th><label for="survey_description">问卷描述</label></th> <td> <textarea id="survey_description" name="survey_description" rows="3" class="large-text"><?php echo esc_textarea($survey->description); ?></textarea> <p class="description">显示在问卷开头的描述文字</p> </td> </tr> <tr> <th><label for="survey_status">状态</label></th> <td> <select id="survey_status" name="survey_status"> <option value="draft" <?php selected($survey->status, 'draft'); ?>>草稿</option> <option value="published" <?php selected($survey->status, 'published'); ?>>已发布</option> <option value="archived" <?php selected($survey->status, 'archived'); ?>>已归档</option> </select> </td> </tr> </table> </div> </div> <!-- 问题管理区域 --> <div class="postbox"> <h2 class="hndle">问卷问题</h2> <div class="inside"> <div id="questions-container" class="sortable-questions"> <?php if (empty($questions)): ?> <p class="no-questions">暂无问题,点击下方"添加问题"按钮开始添加。</p> <?php else: ?> <?php foreach ($questions as $index => $question): ?> <?php $this->render_question_editor($question, $index); ?> <?php endforeach; ?> <?php endif; ?> </div> <div class="add-question-section"> <button type="button" id="add-question" class="button button-primary"> <span class="dashicons dashicons-plus"></span> 添加问题 </button> <div class="question-type-selector" style="display: none;"> <select id="new-question-type"> <option value="text">单行文本</option> <option value="textarea">多行文本</option> <option value="radio">单选题</option> <option value="checkbox">多选题</option> <option value="select">下拉选择</option> <option value="rating">评分题</option> <option value="date">日期选择</option> <option value="email">邮箱地址</option> </select> <button type="button" id="confirm-add-question" class="button">确认添加</button> <button type="button" id="cancel-add-question" class="button button-link">取消</button> </div> </div> </div> </div> </div> <!-- 侧边栏 --> <div id="postbox-container-1" class="postbox-container"> <div class="postbox"> <h2 class="hndle">发布</h2> <div class="inside"> <div class="submitbox"> <div id="major-publishing-actions"> <div id="publishing-action"> <span class="spinner"></span> <input type="submit" name="save_survey" value="保存问卷" class="button button-primary button-large"> </div> <div class="clear"></div> </div> </div> </div> </div> <div class="postbox"> <h2 class="hndle">问卷短码</h2> <div class="inside"> <p>将以下短码插入到文章或页面中显示此问卷:</p> <input type="text" value="[custom_survey id='<?php echo $survey_id; ?>']" class="large-text code" readonly onclick="this.select();"> <p class="description">复制此短码并粘贴到任何文章或页面中</p> </div> </div> <div class="postbox"> <h2 class="hndle">问卷设置</h2> <div class="inside"> <table class="form-table"> <tr> <th><label for="allow_anonymous">允许匿名提交</label></th> <td> <input type="checkbox" id="allow_anonymous" name="allow_anonymous" value="1" checked> </td> </tr> <tr> <th><label for="limit_one_response">限制每人提交一次</label></th> <td> <input type="checkbox" id="limit_one_response" name="limit_one_response" value="1"> </td> </tr> <tr> <th><label for="show_progress">显示进度条</label></th> <td> <input type="checkbox" id="show_progress" name="show_progress" value="1" checked> </td> </tr> </table> </div> </div> </div> </div> </div> </form> </div> <?php } private function render_question_editor($question, $index) { $options = json_decode($question->options, true); ?> <div class="question-editor" data-index="<?php echo $index; ?>" data-id="<?php echo $question->id; ?>"> <div class="question-header"> <span class="question-number">问题 <?php echo $index + 1; ?></span> <span class="question-type-badge"><?php echo $this->get_question_type_label($question->question_type); ?></span> <button type="button" class="delete-question" title="删除问题"> <span class="dashicons dashicons-trash"></span> </button> <button type="button" class="toggle-question" title="展开/收起"> <span class="dashicons dashicons-arrow-down"></span> </button> </div> <div class="question-body"> <div class="question-main"> <input type="hidden" name="questions[<?php echo $index; ?>][id]" value="<?php echo $question->id; ?>"> <input type="hidden" name="questions[<?php echo $index; ?>][type]" value="<?php echo $question->question_type; ?>"> <div class="form-field"> <label>问题内容</label> <input type="text" name="questions[<?php echo $index; ?>][text]" value="<?php echo esc_attr($question->question_text); ?>" class="regular-text question-text" required> </div> <div class="form-field"> <label> <input type="checkbox" name="questions[<?php echo $index; ?>][required]" value="1" <?php checked($question->required, 1); ?>> 必填问题 </label> </div> <!-- 选项配置区域(根据问题类型显示) --> <div class="question-options" data-type="<?php echo $question->question_type; ?>"> <?php $this->render_question_options($question->question_type, $options, $index); ?> </div> </div> </div> </div> <?php } private function render_question_options($type, $options, $index) { switch ($type) { case 'radio': case 'checkbox': case 'select': ?> <div class="form-field"> <label>选项(每行一个)</label> <textarea name="questions[<?php echo $index; ?>][options]" rows="5" class="large-text"><?php if (is_array($options)) { echo implode("n", $options); } ?></textarea> <p class="description">每个选项单独一行</p> </div> <?php break; case 'rating': $min = $options['min'] ?? 1; $max = $options['max'] ?? 5; $labels = $options['labels'] ?? array(); ?>
在当今数据驱动的互联网时代,问卷调查已成为网站收集用户反馈、进行市场研究、评估客户满意度的重要工具。虽然WordPress生态中有许多现成的问卷调查插件,如WPForms、Gravity Forms等,但这些通用解决方案往往无法完全满足特定业务需求。自定义问卷调查工具能够提供更精准的数据收集、更符合品牌形象的用户界面以及更灵活的集成能力。
通过WordPress代码二次开发实现自定义问卷调查工具,不仅可以节省长期使用付费插件的成本,还能确保工具完全按照您的业务流程和需求定制。本指南将详细介绍如何从零开始,为WordPress网站开发一个功能完善的自定义在线问卷调查工具。
在开始开发之前,需要确保具备以下环境:
- 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境
- 代码编辑器:Visual Studio Code、PHPStorm或Sublime Text
- 版本控制:Git用于代码版本管理
- 浏览器开发者工具:用于调试前端代码
在编写代码之前,明确问卷调查工具的核心需求:
- 问卷创建与管理:后台界面用于创建、编辑和删除问卷
- 问题类型支持:单选题、多选题、文本题、评分题等
- 响应收集与存储:安全地存储用户提交的问卷数据
- 数据分析与导出:后台查看统计结果并导出数据
- 前端展示与交互:用户友好的问卷填写界面
- 权限控制:限制问卷访问权限(公开/私有)
设计合理的数据库结构是开发成功的关键。我们需要创建以下数据表:
-
surveys表:存储问卷基本信息
- id (主键)
- title (问卷标题)
- description (问卷描述)
- status (状态:草稿/发布/归档)
- created_at (创建时间)
- updated_at (更新时间)
-
questions表:存储问题信息
- id (主键)
- survey_id (关联问卷ID)
- question_text (问题文本)
- question_type (问题类型:单选/多选/文本等)
- options (选项,JSON格式存储)
- required (是否必答)
- sort_order (排序)
-
responses表:存储用户回答
- id (主键)
- survey_id (问卷ID)
- question_id (问题ID)
- answer (用户答案)
- user_id (用户ID,匿名则为空)
- submitted_at (提交时间)
- session_id (会话ID,用于匿名用户跟踪)
在WordPress的wp-content/plugins/目录下创建新文件夹custom-survey-tool,并创建以下基础文件:
custom-survey-tool/
├── custom-survey-tool.php # 主插件文件
├── includes/
│ ├── class-database.php # 数据库处理类
│ ├── class-survey.php # 问卷核心类
│ ├── class-admin.php # 后台管理类
│ └── class-frontend.php # 前端展示类
├── admin/
│ ├── css/
│ │ └── admin-style.css # 后台样式
│ └── js/
│ └── admin-script.js # 后台脚本
├── public/
│ ├── css/
│ │ └── public-style.css # 前端样式
│ └── js/
│ └── public-script.js # 前端脚本
├── templates/ # 模板文件
└── assets/ # 静态资源
编辑custom-survey-tool.php文件,添加插件基本信息:
<?php
/**
* Plugin Name: 自定义问卷调查工具
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress网站创建自定义在线问卷调查工具
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: custom-survey-tool
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CST_VERSION', '1.0.0');
define('CST_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('CST_PLUGIN_URL', plugin_dir_url(__FILE__));
// 自动加载类文件
spl_autoload_register(function ($class_name) {
$prefix = 'CST_';
$base_dir = CST_PLUGIN_DIR . 'includes/';
$len = strlen($prefix);
if (strncmp($prefix, $class_name, $len) !== 0) {
return;
}
$relative_class = substr($class_name, $len);
$file = $base_dir . 'class-' . strtolower(str_replace('_', '-', $relative_class)) . '.php';
if (file_exists($file)) {
require_once $file;
}
});
// 初始化插件
function cst_init() {
// 检查WordPress版本
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
wp_die(__('本插件需要WordPress 5.0或更高版本', 'custom-survey-tool'));
}
// 实例化核心类
$database = new CST_Database();
$survey = new CST_Survey();
$admin = new CST_Admin();
$frontend = new CST_Frontend();
// 激活/停用钩子
register_activation_hook(__FILE__, array($database, 'create_tables'));
register_deactivation_hook(__FILE__, array($database, 'drop_tables'));
}
add_action('plugins_loaded', 'cst_init');
创建includes/class-database.php文件,处理数据库表的创建与维护:
<?php
class CST_Database {
public function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_prefix = $wpdb->prefix . 'cst_';
// 创建问卷表
$surveys_table = $table_prefix . 'surveys';
$sql1 = "CREATE TABLE IF NOT EXISTS $surveys_table (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(255) NOT NULL,
description text,
status varchar(20) DEFAULT 'draft',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// 创建问题表
$questions_table = $table_prefix . 'questions';
$sql2 = "CREATE TABLE IF NOT EXISTS $questions_table (
id int(11) NOT NULL AUTO_INCREMENT,
survey_id int(11) NOT NULL,
question_text text NOT NULL,
question_type varchar(50) NOT NULL,
options text,
required tinyint(1) DEFAULT 0,
sort_order int(11) DEFAULT 0,
PRIMARY KEY (id),
KEY survey_id (survey_id)
) $charset_collate;";
// 创建回答表
$responses_table = $table_prefix . 'responses';
$sql3 = "CREATE TABLE IF NOT EXISTS $responses_table (
id int(11) NOT NULL AUTO_INCREMENT,
survey_id int(11) NOT NULL,
question_id int(11) NOT NULL,
answer text NOT NULL,
user_id int(11) DEFAULT NULL,
session_id varchar(100) DEFAULT NULL,
submitted_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY survey_id (survey_id),
KEY question_id (question_id),
KEY user_id (user_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql1);
dbDelta($sql2);
dbDelta($sql3);
// 添加默认数据(示例问卷)
$this->add_sample_data();
}
private function add_sample_data() {
global $wpdb;
$table_prefix = $wpdb->prefix . 'cst_';
// 检查是否已有数据
$count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_prefix}surveys");
if ($count == 0) {
// 添加示例问卷
$wpdb->insert(
$table_prefix . 'surveys',
array(
'title' => '客户满意度调查',
'description' => '帮助我们改进服务的简短问卷',
'status' => 'published'
)
);
$survey_id = $wpdb->insert_id;
// 添加示例问题
$sample_questions = array(
array(
'survey_id' => $survey_id,
'question_text' => '您如何评价我们的产品质量?',
'question_type' => 'rating',
'options' => json_encode(array('min' => 1, 'max' => 5, 'labels' => array('非常差', '差', '一般', '好', '非常好'))),
'required' => 1,
'sort_order' => 1
),
array(
'survey_id' => $survey_id,
'question_text' => '您是通过什么渠道了解到我们的?',
'question_type' => 'checkbox',
'options' => json_encode(array('搜索引擎', '社交媒体', '朋友推荐', '广告', '其他')),
'required' => 0,
'sort_order' => 2
),
array(
'survey_id' => $survey_id,
'question_text' => '您有什么改进建议?',
'question_type' => 'textarea',
'options' => json_encode(array('rows' => 4)),
'required' => 0,
'sort_order' => 3
)
);
foreach ($sample_questions as $question) {
$wpdb->insert($table_prefix . 'questions', $question);
}
}
}
public function drop_tables() {
global $wpdb;
$tables = array(
$wpdb->prefix . 'cst_surveys',
$wpdb->prefix . 'cst_questions',
$wpdb->prefix . 'cst_responses'
);
foreach ($tables as $table) {
$wpdb->query("DROP TABLE IF EXISTS $table");
}
}
}
创建includes/class-survey.php文件,实现问卷的核心业务逻辑:
<?php
class CST_Survey {
private $db;
public function __construct() {
global $wpdb;
$this->db = $wpdb;
$this->table_prefix = $wpdb->prefix . 'cst_';
}
// 获取所有问卷
public function get_all_surveys($status = null) {
$where = '';
if ($status) {
$where = $this->db->prepare("WHERE status = %s", $status);
}
return $this->db->get_results(
"SELECT * FROM {$this->table_prefix}surveys $where ORDER BY created_at DESC"
);
}
// 获取单个问卷
public function get_survey($id) {
return $this->db->get_row(
$this->db->prepare(
"SELECT * FROM {$this->table_prefix}surveys WHERE id = %d",
$id
)
);
}
// 获取问卷的所有问题
public function get_survey_questions($survey_id) {
return $this->db->get_results(
$this->db->prepare(
"SELECT * FROM {$this->table_prefix}questions
WHERE survey_id = %d
ORDER BY sort_order ASC",
$survey_id
)
);
}
// 创建新问卷
public function create_survey($data) {
$defaults = array(
'title' => '新问卷',
'description' => '',
'status' => 'draft'
);
$data = wp_parse_args($data, $defaults);
$this->db->insert(
$this->table_prefix . 'surveys',
$data
);
return $this->db->insert_id;
}
// 更新问卷
public function update_survey($id, $data) {
return $this->db->update(
$this->table_prefix . 'surveys',
$data,
array('id' => $id)
);
}
// 删除问卷
public function delete_survey($id) {
// 先删除相关问题
$this->db->delete(
$this->table_prefix . 'questions',
array('survey_id' => $id)
);
// 再删除问卷
return $this->db->delete(
$this->table_prefix . 'surveys',
array('id' => $id)
);
}
// 添加问题到问卷
public function add_question($survey_id, $data) {
$defaults = array(
'survey_id' => $survey_id,
'question_text' => '新问题',
'question_type' => 'text',
'options' => '',
'required' => 0,
'sort_order' => 0
);
$data = wp_parse_args($data, $defaults);
$this->db->insert(
$this->table_prefix . 'questions',
$data
);
return $this->db->insert_id;
}
// 提交问卷回答
public function submit_response($survey_id, $responses, $user_id = null) {
$session_id = $this->generate_session_id();
foreach ($responses as $question_id => $answer) {
// 处理多选答案
if (is_array($answer)) {
$answer = json_encode($answer);
}
$this->db->insert(
$this->table_prefix . 'responses',
array(
'survey_id' => $survey_id,
'question_id' => $question_id,
'answer' => $answer,
'user_id' => $user_id,
'session_id' => $session_id
)
);
}
return true;
}
// 获取问卷统计
public function get_survey_stats($survey_id) {
$questions = $this->get_survey_questions($survey_id);
$stats = array();
foreach ($questions as $question) {
$answers = $this->db->get_results(
$this->db->prepare(
"SELECT answer FROM {$this->table_prefix}responses
WHERE survey_id = %d AND question_id = %d",
$survey_id,
$question->id
)
);
$question_stats = array(
'question' => $question->question_text,
'type' => $question->question_type,
'total_responses' => count($answers),
'answers' => array()
);
// 根据问题类型统计答案
if ($question->question_type === 'radio' || $question->question_type === 'checkbox') {
$options = json_decode($question->options, true);
$counts = array();
foreach ($options as $option) {
$counts[$option] = 0;
}
foreach ($answers as $answer) {
if ($question->question_type === 'checkbox') {
$user_answers = json_decode($answer->answer, true);
if (is_array($user_answers)) {
foreach ($user_answers as $user_answer) {
if (isset($counts[$user_answer])) {
$counts[$user_answer]++;
}
}
}
} else {
if (isset($counts[$answer->answer])) {
$counts[$answer->answer]++;
}
}
}
$question_stats['answers'] = $counts;
} elseif ($question->question_type === 'rating') {
$ratings = array();
foreach ($answers as $answer) {
$ratings[] = intval($answer->answer);
}
if (!empty($ratings)) {
$question_stats['answers'] = array(
'average' => round(array_sum($ratings) / count($ratings), 2),
'min' => min($ratings),
'max' => max($ratings),
'distribution' => array_count_values($ratings)
);
}
}
$stats[] = $question_stats;
}
return $stats;
}
private function generate_session_id() {
return md5(uniqid(rand(), true) . $_SERVER['REMOTE_ADDR']);
}
}
创建includes/class-admin.php文件,实现后台管理功能:
<?php
class CST_Admin {
private $survey;
public function __construct() {
$this->survey = new CST_Survey();
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 加载脚本和样式
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
// 处理表单提交
add_action('admin_post_cst_save_survey', array($this, 'save_survey'));
add_action('admin_post_cst_delete_survey', array($this, 'delete_survey'));
}
public function add_admin_menu() {
// 主菜单
add_menu_page(
'问卷调查工具',
'问卷调查',
'manage_options',
'custom-survey-tool',
array($this, 'surveys_list_page'),
'dashicons-feedback',
30
);
// 子菜单
add_submenu_page(
'custom-survey-tool',
'所有问卷',
'所有问卷',
'manage_options',
'custom-survey-tool',
array($this, 'surveys_list_page')
);
add_submenu_page(
'custom-survey-tool',
'添加新问卷',
'添加新问卷',
'manage_options',
'cst-add-survey',
array($this, 'add_survey_page')
);
add_submenu_page(
'custom-survey-tool',
'问卷统计',
'问卷统计',
'manage_options',
'cst-survey-stats',
array($this, 'survey_stats_page')
);
add_submenu_page(
'custom-survey-tool',
'插件设置',
'设置',
'manage_options',
'cst-settings',
array($this, 'settings_page')
);
}
public function enqueue_admin_scripts($hook) {
// 只在插件页面加载资源
if (strpos($hook, 'custom-survey-tool') === false &&
strpos($hook, 'cst-') === false) {
return;
}
wp_enqueue_style(
'cst-admin-style',
CST_PLUGIN_URL . 'admin/css/admin-style.css',
array(),
CST_VERSION
);
wp_enqueue_script(
'cst-admin-script',
CST_PLUGIN_URL . 'admin/js/admin-script.js',
array('jquery', 'jquery-ui-sortable'),
CST_VERSION,
true
);
// 本地化脚本
wp_localize_script('cst-admin-script', 'cst_admin', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('cst_admin_nonce'),
'confirm_delete' => __('确定要删除这个问卷吗?此操作不可撤销。', 'custom-survey-tool')
));
}
public function add_admin_menu() {
// 主菜单
add_menu_page(
'问卷调查工具',
'问卷调查',
'manage_options',
'custom-survey-tool',
array($this, 'surveys_list_page'),
'dashicons-feedback',
30
);
// 子菜单
add_submenu_page(
'custom-survey-tool',
'所有问卷',
'所有问卷',
'manage_options',
'custom-survey-tool',
array($this, 'surveys_list_page')
);
add_submenu_page(
'custom-survey-tool',
'添加新问卷',
'添加新问卷',
'manage_options',
'cst-add-survey',
array($this, 'add_survey_page')
);
add_submenu_page(
'custom-survey-tool',
'问卷统计',
'问卷统计',
'manage_options',
'cst-survey-stats',
array($this, 'survey_stats_page')
);
add_submenu_page(
'custom-survey-tool',
'插件设置',
'设置',
'manage_options',
'cst-settings',
array($this, 'settings_page')
);
}
public function enqueue_admin_scripts($hook) {
// 只在插件页面加载资源
if (strpos($hook, 'custom-survey-tool') === false &&
strpos($hook, 'cst-') === false) {
return;
}
wp_enqueue_style(
'cst-admin-style',
CST_PLUGIN_URL . 'admin/css/admin-style.css',
array(),
CST_VERSION
);
wp_enqueue_script(
'cst-admin-script',
CST_PLUGIN_URL . 'admin/js/admin-script.js',
array('jquery', 'jquery-ui-sortable'),
CST_VERSION,
true
);
// 本地化脚本
wp_localize_script('cst-admin-script', 'cst_admin', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('cst_admin_nonce'),
'confirm_delete' => __('确定要删除这个问卷吗?此操作不可撤销。', 'custom-survey-tool')
));
}
public function surveys_list_page() {
$action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : 'list';
$survey_id = isset($_GET['survey_id']) ? intval($_GET['survey_id']) : 0;
switch ($action) {
case 'edit':
$this->edit_survey_page($survey_id);
break;
case 'view':
$this->view_responses_page($survey_id);
break;
default:
$this->display_surveys_list();
}
}
private function display_surveys_list() {
$surveys = $this->survey->get_all_surveys();
?>
<div class="wrap cst-admin">
<h1 class="wp-heading-inline">问卷调查</h1>
<a href="<?php echo admin_url('admin.php?page=cst-add-survey'); ?>" class="page-title-action">
添加新问卷
</a>
<?php if (isset($_GET['message'])): ?>
<div class="notice notice-<?php echo sanitize_text_field($_GET['type'] ?? 'success'); ?> is-dismissible">
<p><?php echo esc_html($_GET['message']); ?></p>
</div>
<?php endif; ?>
<div class="tablenav top">
<div class="alignleft actions">
<select name="status_filter" id="status_filter">
<option value="">所有状态</option>
<option value="draft">草稿</option>
<option value="published">已发布</option>
<option value="archived">已归档</option>
</select>
<button class="button action" id="filter_action">筛选</button>
</div>
<br class="clear">
</div>
<table class="wp-list-table widefat fixed striped surveys">
<thead>
<tr>
<th scope="col" class="column-id">ID</th>
<th scope="col" class="column-title">问卷标题</th>
<th scope="col" class="column-status">状态</th>
<th scope="col" class="column-questions">问题数</th>
<th scope="col" class="column-responses">回答数</th>
<th scope="col" class="column-date">创建时间</th>
<th scope="col" class="column-actions">操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($surveys)): ?>
<tr>
<td colspan="7" class="no-items">暂无问卷,请点击"添加新问卷"创建第一个问卷。</td>
</tr>
<?php else: ?>
<?php foreach ($surveys as $survey):
$questions_count = $this->survey->get_questions_count($survey->id);
$responses_count = $this->survey->get_responses_count($survey->id);
?>
<tr>
<td class="column-id"><?php echo $survey->id; ?></td>
<td class="column-title">
<strong>
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>">
<?php echo esc_html($survey->title); ?>
</a>
</strong>
<div class="row-actions">
<span class="edit">
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>">编辑</a> |
</span>
<span class="view">
<a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>">统计</a> |
</span>
<span class="duplicate">
<a href="#" class="duplicate-survey" data-id="<?php echo $survey->id; ?>">复制</a> |
</span>
<span class="trash">
<a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=cst_delete_survey&survey_id=' . $survey->id), 'cst_delete_survey'); ?>"
class="submitdelete" onclick="return confirm('确定要删除这个问卷吗?')">删除</a>
</span>
</div>
</td>
<td class="column-status">
<span class="status-badge status-<?php echo $survey->status; ?>">
<?php
$status_labels = array(
'draft' => '草稿',
'published' => '已发布',
'archived' => '已归档'
);
echo $status_labels[$survey->status] ?? $survey->status;
?>
</span>
</td>
<td class="column-questions"><?php echo $questions_count; ?></td>
<td class="column-responses"><?php echo $responses_count; ?></td>
<td class="column-date"><?php echo date('Y-m-d H:i', strtotime($survey->created_at)); ?></td>
<td class="column-actions">
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>"
class="button button-small">编辑</a>
<a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>"
class="button button-small">查看统计</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
}
public function surveys_list_page() {
$action = isset($_GET['action']) ? sanitize_text_field($_GET['action']) : 'list';
$survey_id = isset($_GET['survey_id']) ? intval($_GET['survey_id']) : 0;
switch ($action) {
case 'edit':
$this->edit_survey_page($survey_id);
break;
case 'view':
$this->view_responses_page($survey_id);
break;
default:
$this->display_surveys_list();
}
}
private function display_surveys_list() {
$surveys = $this->survey->get_all_surveys();
?>
<div class="wrap cst-admin">
<h1 class="wp-heading-inline">问卷调查</h1>
<a href="<?php echo admin_url('admin.php?page=cst-add-survey'); ?>" class="page-title-action">
添加新问卷
</a>
<?php if (isset($_GET['message'])): ?>
<div class="notice notice-<?php echo sanitize_text_field($_GET['type'] ?? 'success'); ?> is-dismissible">
<p><?php echo esc_html($_GET['message']); ?></p>
</div>
<?php endif; ?>
<div class="tablenav top">
<div class="alignleft actions">
<select name="status_filter" id="status_filter">
<option value="">所有状态</option>
<option value="draft">草稿</option>
<option value="published">已发布</option>
<option value="archived">已归档</option>
</select>
<button class="button action" id="filter_action">筛选</button>
</div>
<br class="clear">
</div>
<table class="wp-list-table widefat fixed striped surveys">
<thead>
<tr>
<th scope="col" class="column-id">ID</th>
<th scope="col" class="column-title">问卷标题</th>
<th scope="col" class="column-status">状态</th>
<th scope="col" class="column-questions">问题数</th>
<th scope="col" class="column-responses">回答数</th>
<th scope="col" class="column-date">创建时间</th>
<th scope="col" class="column-actions">操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($surveys)): ?>
<tr>
<td colspan="7" class="no-items">暂无问卷,请点击"添加新问卷"创建第一个问卷。</td>
</tr>
<?php else: ?>
<?php foreach ($surveys as $survey):
$questions_count = $this->survey->get_questions_count($survey->id);
$responses_count = $this->survey->get_responses_count($survey->id);
?>
<tr>
<td class="column-id"><?php echo $survey->id; ?></td>
<td class="column-title">
<strong>
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>">
<?php echo esc_html($survey->title); ?>
</a>
</strong>
<div class="row-actions">
<span class="edit">
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>">编辑</a> |
</span>
<span class="view">
<a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>">统计</a> |
</span>
<span class="duplicate">
<a href="#" class="duplicate-survey" data-id="<?php echo $survey->id; ?>">复制</a> |
</span>
<span class="trash">
<a href="<?php echo wp_nonce_url(admin_url('admin-post.php?action=cst_delete_survey&survey_id=' . $survey->id), 'cst_delete_survey'); ?>"
class="submitdelete" onclick="return confirm('确定要删除这个问卷吗?')">删除</a>
</span>
</div>
</td>
<td class="column-status">
<span class="status-badge status-<?php echo $survey->status; ?>">
<?php
$status_labels = array(
'draft' => '草稿',
'published' => '已发布',
'archived' => '已归档'
);
echo $status_labels[$survey->status] ?? $survey->status;
?>
</span>
</td>
<td class="column-questions"><?php echo $questions_count; ?></td>
<td class="column-responses"><?php echo $responses_count; ?></td>
<td class="column-date"><?php echo date('Y-m-d H:i', strtotime($survey->created_at)); ?></td>
<td class="column-actions">
<a href="<?php echo admin_url('admin.php?page=custom-survey-tool&action=edit&survey_id=' . $survey->id); ?>"
class="button button-small">编辑</a>
<a href="<?php echo admin_url('admin.php?page=cst-survey-stats&survey_id=' . $survey->id); ?>"
class="button button-small">查看统计</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php
}
private function edit_survey_page($survey_id) {
$survey = $this->survey->get_survey($survey_id);
$questions = $this->survey->get_survey_questions($survey_id);
if (!$survey) {
wp_die('问卷不存在');
}
?>
<div class="wrap cst-admin">
<h1>编辑问卷: <?php echo esc_html($survey->title); ?></h1>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" id="survey-form">
<input type="hidden" name="action" value="cst_save_survey">
<input type="hidden" name="survey_id" value="<?php echo $survey_id; ?>">
<?php wp_nonce_field('cst_save_survey', 'cst_nonce'); ?>
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-2">
<!-- 主内容区 -->
<div id="post-body-content">
<div class="postbox">
<h2 class="hndle">问卷基本信息</h2>
<div class="inside">
<table class="form-table">
<tr>
<th><label for="survey_title">问卷标题</label></th>
<td>
<input type="text" id="survey_title" name="survey_title"
value="<?php echo esc_attr($survey->title); ?>"
class="regular-text" required>
</td>
</tr>
<tr>
<th><label for="survey_description">问卷描述</label></th>
<td>
<textarea id="survey_description" name="survey_description"
rows="3" class="large-text"><?php echo esc_textarea($survey->description); ?></textarea>
<p class="description">显示在问卷开头的描述文字</p>
</td>
</tr>
<tr>
<th><label for="survey_status">状态</label></th>
<td>
<select id="survey_status" name="survey_status">
<option value="draft" <?php selected($survey->status, 'draft'); ?>>草稿</option>
<option value="published" <?php selected($survey->status, 'published'); ?>>已发布</option>
<option value="archived" <?php selected($survey->status, 'archived'); ?>>已归档</option>
</select>
</td>
</tr>
</table>
</div>
</div>
<!-- 问题管理区域 -->
<div class="postbox">
<h2 class="hndle">问卷问题</h2>
<div class="inside">
<div id="questions-container" class="sortable-questions">
<?php if (empty($questions)): ?>
<p class="no-questions">暂无问题,点击下方"添加问题"按钮开始添加。</p>
<?php else: ?>
<?php foreach ($questions as $index => $question): ?>
<?php $this->render_question_editor($question, $index); ?>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="add-question-section">
<button type="button" id="add-question" class="button button-primary">
<span class="dashicons dashicons-plus"></span> 添加问题
</button>
<div class="question-type-selector" style="display: none;">
<select id="new-question-type">
<option value="text">单行文本</option>
<option value="textarea">多行文本</option>
<option value="radio">单选题</option>
<option value="checkbox">多选题</option>
<option value="select">下拉选择</option>
<option value="rating">评分题</option>
<option value="date">日期选择</option>
<option value="email">邮箱地址</option>
</select>
<button type="button" id="confirm-add-question" class="button">确认添加</button>
<button type="button" id="cancel-add-question" class="button button-link">取消</button>
</div>
</div>
</div>
</div>
</div>
<!-- 侧边栏 -->
<div id="postbox-container-1" class="postbox-container">
<div class="postbox">
<h2 class="hndle">发布</h2>
<div class="inside">
<div class="submitbox">
<div id="major-publishing-actions">
<div id="publishing-action">
<span class="spinner"></span>
<input type="submit" name="save_survey"
value="保存问卷" class="button button-primary button-large">
</div>
<div class="clear"></div>
</div>
</div>
</div>
</div>
<div class="postbox">
<h2 class="hndle">问卷短码</h2>
<div class="inside">
<p>将以下短码插入到文章或页面中显示此问卷:</p>
<input type="text" value="[custom_survey id='<?php echo $survey_id; ?>']"
class="large-text code" readonly onclick="this.select();">
<p class="description">复制此短码并粘贴到任何文章或页面中</p>
</div>
</div>
<div class="postbox">
<h2 class="hndle">问卷设置</h2>
<div class="inside">
<table class="form-table">
<tr>
<th><label for="allow_anonymous">允许匿名提交</label></th>
<td>
<input type="checkbox" id="allow_anonymous" name="allow_anonymous" value="1" checked>
</td>
</tr>
<tr>
<th><label for="limit_one_response">限制每人提交一次</label></th>
<td>
<input type="checkbox" id="limit_one_response" name="limit_one_response" value="1">
</td>
</tr>
<tr>
<th><label for="show_progress">显示进度条</label></th>
<td>
<input type="checkbox" id="show_progress" name="show_progress" value="1" checked>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<?php
}
private function render_question_editor($question, $index) {
$options = json_decode($question->options, true);
?>
<div class="question-editor" data-index="<?php echo $index; ?>" data-id="<?php echo $question->id; ?>">
<div class="question-header">
<span class="question-number">问题 <?php echo $index + 1; ?></span>
<span class="question-type-badge"><?php echo $this->get_question_type_label($question->question_type); ?></span>
<button type="button" class="delete-question" title="删除问题">
<span class="dashicons dashicons-trash"></span>
</button>
<button type="button" class="toggle-question" title="展开/收起">
<span class="dashicons dashicons-arrow-down"></span>
</button>
</div>
<div class="question-body">
<div class="question-main">
<input type="hidden" name="questions[<?php echo $index; ?>][id]" value="<?php echo $question->id; ?>">
<input type="hidden" name="questions[<?php echo $index; ?>][type]" value="<?php echo $question->question_type; ?>">
<div class="form-field">
<label>问题内容</label>
<input type="text" name="questions[<?php echo $index; ?>][text]"
value="<?php echo esc_attr($question->question_text); ?>"
class="regular-text question-text" required>
</div>
<div class="form-field">
<label>
<input type="checkbox" name="questions[<?php echo $index; ?>][required]" value="1"
<?php checked($question->required, 1); ?>> 必填问题
</label>
</div>
<!-- 选项配置区域(根据问题类型显示) -->
<div class="question-options" data-type="<?php echo $question->question_type; ?>">
<?php $this->render_question_options($question->question_type, $options, $index); ?>
</div>
</div>
</div>
</div>
<?php
}
private function render_question_options($type, $options, $index) {
switch ($type) {
case 'radio':
case 'checkbox':
case 'select':
?>
<div class="form-field">
<label>选项(每行一个)</label>
<textarea name="questions[<?php echo $index; ?>][options]"
rows="5" class="large-text"><?php
if (is_array($options)) {
echo implode("n", $options);
}
?></textarea>
<p class="description">每个选项单独一行</p>
</div>
<?php
break;
case 'rating':
$min = $options['min'] ?? 1;
$max = $options['max'] ?? 5;
$labels = $options['labels'] ?? array();
?>
private function edit_survey_page($survey_id) {
$survey = $this->survey->get_survey($survey_id);
$questions = $this->survey->get_survey_questions($survey_id);
if (!$survey) {
wp_die('问卷不存在');
}
?>
<div class="wrap cst-admin">
<h1>编辑问卷: <?php echo esc_html($survey->title); ?></h1>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" id="survey-form">
<input type="hidden" name="action" value="cst_save_survey">
<input type="hidden" name="survey_id" value="<?php echo $survey_id; ?>">
<?php wp_nonce_field('cst_save_survey', 'cst_nonce'); ?>
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-2">
<!-- 主内容区 -->
<div id="post-body-content">
<div class="postbox">
<h2 class="hndle">问卷基本信息</h2>
<div class="inside">
<table class="form-table">
<tr>
<th><label for="survey_title">问卷标题</label></th>
<td>
<input type="text" id="survey_title" name="survey_title"
value="<?php echo esc_attr($survey->title); ?>"
class="regular-text" required>
</td>
</tr>
<tr>
<th><label for="survey_description">问卷描述</label></th>
<td>
<textarea id="survey_description" name="survey_description"
rows="3" class="large-text"><?php echo esc_textarea($survey->description); ?></textarea>
<p class="description">显示在问卷开头的描述文字</p>
</td>
</tr>
<tr>
<th><label for="survey_status">状态</label></th>
<td>
<select id="survey_status" name="survey_status">
<option value="draft" <?php selected($survey->status, 'draft'); ?>>草稿</option>
<option value="published" <?php selected($survey->status, 'published'); ?>>已发布</option>
<option value="archived" <?php selected($survey->status, 'archived'); ?>>已归档</option>
</select>
</td>
</tr>
</table>
</div>
</div>
<!-- 问题管理区域 -->
<div class="postbox">
<h2 class="hndle">问卷问题</h2>
<div class="inside">
<div id="questions-container" class="sortable-questions">
<?php if (empty($questions)): ?>
<p class="no-questions">暂无问题,点击下方"添加问题"按钮开始添加。</p>
<?php else: ?>
<?php foreach ($questions as $index => $question): ?>
<?php $this->render_question_editor($question, $index); ?>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="add-question-section">
<button type="button" id="add-question" class="button button-primary">
<span class="dashicons dashicons-plus"></span> 添加问题
</button>
<div class="question-type-selector" style="display: none;">
<select id="new-question-type">
<option value="text">单行文本</option>
<option value="textarea">多行文本</option>
<option value="radio">单选题</option>
<option value="checkbox">多选题</option>
<option value="select">下拉选择</option>
<option value="rating">评分题</option>
<option value="date">日期选择</option>
<option value="email">邮箱地址</option>
</select>
<button type="button" id="confirm-add-question" class="button">确认添加</button>
<button type="button" id="cancel-add-question" class="button button-link">取消</button>
</div>
</div>
</div>
</div>
</div>
<!-- 侧边栏 -->
<div id="postbox-container-1" class="postbox-container">
<div class="postbox">
<h2 class="hndle">发布</h2>
<div class="inside">
<div class="submitbox">
<div id="major-publishing-actions">
<div id="publishing-action">
<span class="spinner"></span>
<input type="submit" name="save_survey"
value="保存问卷" class="button button-primary button-large">
</div>
<div class="clear"></div>
</div>
</div>
</div>
</div>
<div class="postbox">
<h2 class="hndle">问卷短码</h2>
<div class="inside">
<p>将以下短码插入到文章或页面中显示此问卷:</p>
<input type="text" value="[custom_survey id='<?php echo $survey_id; ?>']"
class="large-text code" readonly onclick="this.select();">
<p class="description">复制此短码并粘贴到任何文章或页面中</p>
</div>
</div>
<div class="postbox">
<h2 class="hndle">问卷设置</h2>
<div class="inside">
<table class="form-table">
<tr>
<th><label for="allow_anonymous">允许匿名提交</label></th>
<td>
<input type="checkbox" id="allow_anonymous" name="allow_anonymous" value="1" checked>
</td>
</tr>
<tr>
<th><label for="limit_one_response">限制每人提交一次</label></th>
<td>
<input type="checkbox" id="limit_one_response" name="limit_one_response" value="1">
</td>
</tr>
<tr>
<th><label for="show_progress">显示进度条</label></th>
<td>
<input type="checkbox" id="show_progress" name="show_progress" value="1" checked>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
<?php
}
private function render_question_editor($question, $index) {
$options = json_decode($question->options, true);
?>
<div class="question-editor" data-index="<?php echo $index; ?>" data-id="<?php echo $question->id; ?>">
<div class="question-header">
<span class="question-number">问题 <?php echo $index + 1; ?></span>
<span class="question-type-badge"><?php echo $this->get_question_type_label($question->question_type); ?></span>
<button type="button" class="delete-question" title="删除问题">
<span class="dashicons dashicons-trash"></span>
</button>
<button type="button" class="toggle-question" title="展开/收起">
<span class="dashicons dashicons-arrow-down"></span>
</button>
</div>
<div class="question-body">
<div class="question-main">
<input type="hidden" name="questions[<?php echo $index; ?>][id]" value="<?php echo $question->id; ?>">
<input type="hidden" name="questions[<?php echo $index; ?>][type]" value="<?php echo $question->question_type; ?>">
<div class="form-field">
<label>问题内容</label>
<input type="text" name="questions[<?php echo $index; ?>][text]"
value="<?php echo esc_attr($question->question_text); ?>"
class="regular-text question-text" required>
</div>
<div class="form-field">
<label>
<input type="checkbox" name="questions[<?php echo $index; ?>][required]" value="1"
<?php checked($question->required, 1); ?>> 必填问题
</label>
</div>
<!-- 选项配置区域(根据问题类型显示) -->
<div class="question-options" data-type="<?php echo $question->question_type; ?>">
<?php $this->render_question_options($question->question_type, $options, $index); ?>
</div>
</div>
</div>
</div>
<?php
}
private function render_question_options($type, $options, $index) {
switch ($type) {
case 'radio':
case 'checkbox':
case 'select':
?>
<div class="form-field">
<label>选项(每行一个)</label>
<textarea name="questions[<?php echo $index; ?>][options]"
rows="5" class="large-text"><?php
if (is_array($options)) {
echo implode("n", $options);
}
?></textarea>
<p class="description">每个选项单独一行</p>
</div>
<?php
break;
case 'rating':
$min = $options['min'] ?? 1;
$max = $options['max'] ?? 5;
$labels = $options['labels'] ?? array();
?>


