承接上文,我們制作了一個簡單的,帶有兩個輸入框和一個保存按鈕的設置選項,現在,他已經能實現了最基礎的填寫信息并保存到 WordPress 后臺,供PHP調用選項值的功能。
現在,我們更進一步,為其添加人員篩選功能,我們制作一個下列框,從中通過用戶名進行選擇,并將選擇好的用戶 ID 通過數組提供給后端使用。大概流程如下

最終效果如下

準備用戶數據
一般的網站用戶量較大,大部分都是“訂閱者”,為了減少傳輸數據壓力,我們在獲取用戶數據時將其排除掉
為了將拿到數據方便給 JS 使用,降低 JS 使用數據難度,我們將其整理成如下結構

在 vue-spa.php
文件底部添加以下代碼
//整理并提供用戶信息
function vuespa_get_user_meat()
{
//獲取所有角色
$editable_roles = wp_roles()->roles;
$roles = array_keys($editable_roles);
//獲取除了'subscriber'(訂閱者)角色之外的所有角色的用戶數據
$subscriber_key = array_search('subscriber', $roles, true);
if (false !== $subscriber_key) {
$roles = array_slice($roles, 0, $subscriber_key);
}
$users = get_users(array('role__in' => $roles));
//轉為關聯數組
$user_data = array_map(function ($user) {
return [
'id' => $user->ID,
'name' => $user->display_name,
];
}, $users);
return $user_data;
}
您可以參考此方法,做出分類篩選、文章篩選、標簽篩選等篩選功能,只需按結構提供數據即可。
傳遞用戶數據
我們通過 PHP 將數據傳給 JS ,以供使用,我們修改 vue-spa.php
文件中的 vuespa_data()
函數,改為以下內容
function vuespa_data()
{
$person = [
"str" => "Hello, world! - Npcink",
"num" => 25,
"city" => [1, 2, 3, 4, 5],
"user" => vuespa_get_user_meat(),
];
return $person;
}
刷新菜單頁面,我們就能看到如上圖的效果。
JS 準備頁面
JS 中拿到傳來的數據,需要將其渲染至頁面上,修改index.js為以下內容
//vite/dist/index.js
console.log(dataLocal.data.user);
const App = {
setup() {
//存儲傳來的值
const siteData = dataLocal.data;
//存儲選項值
const datas = Vue.reactive({
dataOne: "",
dataTwo: "",
dataName: [],
});
//獲取數據
const vuespa_get_option = () => {
axios
.post(dataLocal.route + "pf/v1/get_option", datas, {
headers: {
"X-WP-Nonce": dataLocal.nonce,
"Content-Type": "application/json",
},
})
.then((response) => {
const data = response.data;
datas.dataOne = data.dataOne;
datas.dataTwo = data.dataTwo;
datas.dataName = data.dataName;
})
.catch((error) => {
window.alert("連接服務器失敗或后臺讀取出錯!數據讀取失敗");
console.log(error);
});
};
//省略部分代碼
return { datas, siteData, vuespa_update_option };
},
template: `
文本框1:<input type="text" v-model="datas.dataOne"><br/>
文本框2:<input type="text" v-model="datas.dataTwo"><hr/>
用戶選擇:<select v-model="datas.dataName" multiple>
<option v-for="option in siteData.user" :key="option.id" :value="option.id">
{{ option.name }}
</option>
</select>
<p>你選擇了:{{ datas.dataName }}</p><br/>
按住command(control)按鍵即可進行多選<hr/>
<button class="button button-primary" @click="vuespa_update_option">保存</button>`,
};
Vue.createApp(App).mount("#vuespa");
為了方便大家看出不同,我省略了部分未修改的代碼,其詳細內容,可見上一節。
主要內容如下
- 新建變量
siteData
存儲傳來的數據 - 新建數組變量
dataName
存儲選中數組 - 在獲取數據中,通過
datas.dataName = data.dataName;
拿到默認值
修改保存接口
原有的保存接口無法識別數組,若您此時修改選中的值并點擊保存按鈕,刷新頁面后會丟失選中的值。
修改 interface.php
文章的保存選項功能函數 update_option_by_RestAPI()
為以下內容
//保存Option
function update_option_by_RestAPI($data)
{
//判斷是否是管理員
if (current_user_can('manage_options')) {
//轉為JSON對象 - 重點,這里沒有true,是轉為對象
$dataArray = json_decode($data->get_body());
//存儲結果
$result = new stdClass();
//循環保存選項
foreach ($dataArray as $option_name => $value) {
//判斷,是否為對象
if (is_object($value)) {
//是非空數組,循環保存值
foreach ($value as $arr => $data) {
//更新值
update_option($arr, $data);
}
} else {
//不是對象,則表示只有一個選項需要保存。
update_option($option_name, $value);
}
$result->$option_name = $value;
}
//返回成功信息
return new WP_REST_Response(array(
'success' => true,
'message' => "已保存!"
), 200);
} else {
//返回失敗信息
return new WP_Error('save_error', '保存失??!', array('status' => 500));
}
}
此函數的功能可見注釋,現在,刷新頁面,在下列列表中進行篩選,然后點擊保存按鈕,刷新頁面,可看到值是正常保存的,
使用
此時,您可以使用 get_option
在PHP中拿到選項中的值,正如上一節中提到的。
echo get_option('dataName');
注意,這會展示數組ID
總結
這一節我們添加了下拉列表多選功能,并沒有解決上一節提到的問題,不要急,我發現目前展示功能類型很方便,我準備在展示完所有常見功能類型后,再去解決上一節提到的問題。
文件代碼
vue-spa.php
<?php
/*
Plugin Name: Vue - SPA
Plugin URI: http://www.kartiktrivedi.com
Description: 將vue構建的頁面嵌入WordPress 中并產生交互
Author: Muze
Author URI: http://www.kartiktrivedi.com
Version: 1.0.0
*/
//接口
require_once plugin_dir_path(__FILE__) . 'interface.php';
//創建一個菜單
function vuespa_create_menu_page()
{
add_menu_page(
'VueSpa選項', // 此菜單對應頁面上顯示的標題
'VueSpa', // 要為此實際菜單項顯示的文本
'administrator', // 哪種類型的用戶可以看到此菜單
'vuespa_id', // 此菜單項的唯一ID(即段塞)
'vuespa_menu_page_display', // 呈現此頁面的菜單時要調用的函數的名稱
'dashicons-admin-customizer', //圖標 - 默認圖標
'500.1', //位置
);
} // end vuespa_create_menu_page
add_action('admin_menu', 'vuespa_create_menu_page');
//菜單回調 - 展示的內容
function vuespa_menu_page_display()
{
?>
<!--在默認WordPress“包裝”容器中創建標題-->
<div class="wrap">
<!--標題-->
<h2><?php echo esc_html(get_admin_page_title()); ?></h2>
<!--提供Vue掛載點-->
<div id="vuespa">此內容將在掛載Vue后被替換{{data}}</div>
</div>
<?php
//展示準備的數據
echo "<pre>";
print_r(vuespa_data());
echo "</pre>";
echo "<h3>調用選項值</h3>";
echo get_option('dataOne');
echo "<br/>";
echo get_option('dataTwo');
} // vuespa_menu_page_display
//載入所需 JS 和 CSS 資源 并傳遞數據
function vuespa_load_vues($hook)
{
//判斷當前頁面是否是指定頁面,是則繼續加載
if ('toplevel_page_vuespa_id' != $hook) {
return;
}
//版本號
$ver = '53';
//加載到頁面頂部
wp_enqueue_style('vite', plugin_dir_url(__FILE__) . 'vite/dist/index.css', array(), $ver, false);
//加載到頁面底部
wp_enqueue_script('vue', 'https://unpkg.com/vue@3/dist/vue.global.js', array(), $ver, true);
wp_enqueue_script('axios', 'https://unpkg.com/axios/dist/axios.min.js', array(), $ver, true);
wp_enqueue_script('vite', plugin_dir_url(__FILE__) . 'vite/dist/index.js', array(), $ver, true);
$pf_api_translation_array = array(
'route' => esc_url_raw(rest_url()), //路由
'nonce' => wp_create_nonce('wp_rest'), //驗證標記
'data' => vuespa_data(), //自定義數據
);
wp_localize_script('vite', 'dataLocal', $pf_api_translation_array); //傳給vite項目
}
//樣式加載到后臺
add_action('admin_enqueue_scripts', 'vuespa_load_vues');
//準備待傳輸的數據
function vuespa_data()
{
$person = [
"str" => "Hello, world! - Npcink",
"num" => 25,
"city" => [1, 2, 3, 4, 5],
"user" => vuespa_get_user_meat(),
];
return $person;
}
//整理并提供用戶信息
function vuespa_get_user_meat()
{
//獲取所有角色
$editable_roles = wp_roles()->roles;
$roles = array_keys($editable_roles);
//獲取除了'subscriber'(訂閱者)角色之外的所有角色的用戶數據
$subscriber_key = array_search('subscriber', $roles, true);
if (false !== $subscriber_key) {
$roles = array_slice($roles, 0, $subscriber_key);
}
$users = get_users(array('role__in' => $roles));
//轉為關聯數組
$user_data = array_map(function ($user) {
return [
'id' => $user->ID,
'name' => $user->display_name,
];
}, $users);
return $user_data;
}
interface.php
<?php
//interface.php
//接口文件
function vuespa_create_api()
{
register_rest_route('pf/v1', '/get_option/', array( // 完整命名空間為:/wp-json/pf/v1/
'methods' => 'POST',
'callback' => 'get_option_by_RestAPI',
));
register_rest_route('pf/v1', '/update_option/', array( // 完整命名空間為:/wp-json/pf/v1/
'methods' => 'POST',
'callback' => 'update_option_by_RestAPI',
'permission_callback' => function () {
return current_user_can('manage_options'); // 只有管理員才有權限修改
},
));
}
add_action('rest_api_init', 'vuespa_create_api');
//讀取Option
//僅支持一對一的數據請求
function get_option_by_RestAPI($data)
{
//將傳遞數據轉成數組類型
$dataArray = json_decode($data->get_body(), true);
//新建數組
$return = array();
//循環獲取對應選項ID的值,并將其存儲在對應關聯數組中,若拿不到值,則為空
foreach ($dataArray as $option_name => $value) {
$return[$option_name] = get_option($option_name) ? get_option($option_name) : "";
}
return $return;
}
//保存Option
function update_option_by_RestAPI($data)
{
//判斷是否是管理員
if (current_user_can('manage_options')) {
//轉為JSON對象 - 重點,這里沒有true,是轉為對象
$dataArray = json_decode($data->get_body());
//存儲結果
$result = new stdClass();
//循環保存選項
foreach ($dataArray as $option_name => $value) {
//判斷,是否為對象
if (is_object($value)) {
//是非空數組,循環保存值
foreach ($value as $arr => $data) {
//更新值
update_option($arr, $data);
}
} else {
//不是對象,則表示只有一個選項需要保存。
update_option($option_name, $value);
}
$result->$option_name = $value;
}
//返回成功信息
return new WP_REST_Response(array(
'success' => true,
'message' => "已保存!"
), 200);
} else {
//返回失敗信息
return new WP_Error('save_error', '保存失??!', array('status' => 500));
}
}
index.js
//vite/dist/index.js
console.log(dataLocal.data.user);
const App = {
setup() {
//存儲傳來的值
const siteData = dataLocal.data;
//存儲選項值
const datas = Vue.reactive({
dataOne: "",
dataTwo: "",
dataName: [],
});
//獲取數據
const vuespa_get_option = () => {
axios
.post(dataLocal.route + "pf/v1/get_option", datas, {
headers: {
"X-WP-Nonce": dataLocal.nonce,
"Content-Type": "application/json",
},
})
.then((response) => {
const data = response.data;
datas.dataOne = data.dataOne;
datas.dataTwo = data.dataTwo;
datas.dataName = data.dataName;
})
.catch((error) => {
window.alert("連接服務器失敗或后臺讀取出錯!數據讀取失敗");
console.log(error);
});
};
//保存數據
const vuespa_update_option = () => {
axios
.post(dataLocal.route + "pf/v1/update_option", datas, {
headers: {
"X-WP-Nonce": dataLocal.nonce,
},
})
.then((response) => {
alert("保存成功");
})
.catch((error) => {
alert("保存失敗");
console.log(error);
});
};
//頁面初始加載
Vue.onMounted(() => {
console.log("簡簡單單");
vuespa_get_option();
});
return { datas, siteData, vuespa_update_option };
},
template: `
文本框1:<input type="text" v-model="datas.dataOne"><br/>
文本框2:<input type="text" v-model="datas.dataTwo"><hr/>
用戶選擇:<select v-model="datas.dataName" multiple>
<option v-for="option in siteData.user" :key="option.id" :value="option.id">
{{ option.name }}
</option>
</select>
<p>你選擇了:{{ datas.dataName }}</p><br/>
按住command(control)按鍵即可進行多選<hr/>
<button class="button button-primary" @click="vuespa_update_option">保存</button>`,
};
Vue.createApp(App).mount("#vuespa");