diff --git a/plugins/addon/theme_configurator/README.md b/plugins/addon/theme_configurator/README.md new file mode 100644 index 0000000..31b9c9f --- /dev/null +++ b/plugins/addon/theme_configurator/README.md @@ -0,0 +1,30 @@ +# Theme Configurator 插件 + +此插件演示如何通过后台插件的方式为 BlackFruit-UI 主题提供可配置能力,支持设置导航、页脚、站点信息、SEO、首页轮播以及右侧浮窗,并提供 `/console/v1/theme/config` 接口与前端联动。 + +## 功能 +- 后台界面(`template/admin/index.php`)以 JSON 的形式集中维护所有主题参数; +- 接口 `GET/POST /{DIR_ADMIN}/v1/theme/config` 提供配置读取与保存; +- 前台接口 `GET /console/v1/theme/config` 输出与 `/console/v1/common` 相同结构的数据,BlackFruit-UI 可以直接接入; +- 插件安装时创建 `addon_theme_configurator` 表并写入默认配置。 + +## 目录 +``` +plugins/addon/theme_configurator +├── ThemeConfigurator.php # 插件主文件 +├── controller/ +│ ├── ThemeController.php # 后台 API +│ └── clientarea/ThemeController.php # 前台 API +├── model/ThemeConfigModel.php # 主题配置模型 +├── template/admin/index.php # 后台可视化页面 +├── route.php # 自定义路由定义 +├── sidebar*.php # 前后台导航 +├── auth.php # 权限配置 +└── lang/zh-cn.php # 多语言 +``` + +## 使用步骤 +1. 将目录复制到业务系统的 `public/plugins/addon` 下; +2. 在后台启用插件,安装脚本会自动创建 `addon_theme_configurator` 表; +3. 进入“插件 > 主题配置”页,按 JSON 结构维护导航、SEO、轮播、侧边栏等; +4. 前端 BlackFruit-UI 请求 `/console/v1/theme/config` 以获取运行时配置。若需要兼容现有 `/console/v1/common`,可在 Nginx 或网关层做转发。 diff --git a/plugins/addon/theme_configurator/ThemeConfigurator.php b/plugins/addon/theme_configurator/ThemeConfigurator.php new file mode 100644 index 0000000..5d0f1ca --- /dev/null +++ b/plugins/addon/theme_configurator/ThemeConfigurator.php @@ -0,0 +1,80 @@ + 'ThemeConfigurator', + 'title' => '主题可视化配置', + 'description' => '通过插件管理 BlackFruit-UI 导航、页脚、SEO、轮播与侧边栏等主题参数', + 'author' => 'yiqiu', + 'version' => '1.0.0', + ]; + + /** @var bool 标记不需要默认插件导航 */ + public $noNav = false; + + /** + * 安装插件 - 初始化存储表以及默认配置 + */ + public function install() + { + try { + Db::execute( + "CREATE TABLE IF NOT EXISTS `addon_theme_configurator`( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `config` longtext NOT NULL, + `update_time` int(11) DEFAULT 0, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" + ); + } catch (\Throwable $throwable) { + return [ + 'status' => 400, + 'msg' => $throwable->getMessage(), + ]; + } + + (new ThemeConfigModel())->initConfig(); + + return [ + 'status' => 200, + 'msg' => lang_plugins('theme_configurator_install_success'), + ]; + } + + /** + * 卸载插件 - 移除存储表 + */ + public function uninstall() + { + try { + Db::execute("DROP TABLE IF EXISTS `addon_theme_configurator`;"); + } catch (\Throwable $throwable) { + return [ + 'status' => 400, + 'msg' => $throwable->getMessage(), + ]; + } + + return [ + 'status' => 200, + 'msg' => lang_plugins('theme_configurator_uninstall_success'), + ]; + } + + /** + * 供其他业务调用,快速获取主题配置 + */ + public function getThemeConfig() + { + return (new ThemeConfigModel())->getConfig(); + } +} diff --git a/plugins/addon/theme_configurator/auth.php b/plugins/addon/theme_configurator/auth.php new file mode 100644 index 0000000..e6a0bdd --- /dev/null +++ b/plugins/addon/theme_configurator/auth.php @@ -0,0 +1,15 @@ + 'auth_plugin_addon_theme_configurator', + 'url' => 'theme_config', + 'child' => [ + [ + 'title' => 'auth_plugin_addon_theme_configurator_save', + 'url' => '', + 'auth_rule' => 'ThemeController::save', + 'auth_rule_title' => 'auth_plugin_addon_theme_configurator_save', + ], + ], + ], +]; diff --git a/plugins/addon/theme_configurator/controller/ThemeController.php b/plugins/addon/theme_configurator/controller/ThemeController.php new file mode 100644 index 0000000..8c9f4c1 --- /dev/null +++ b/plugins/addon/theme_configurator/controller/ThemeController.php @@ -0,0 +1,58 @@ +model = new ThemeConfigModel(); + } + + /** + * 获取配置 + */ + public function config(): Response + { + return json([ + 'status' => 200, + 'msg' => lang_plugins('theme_configurator_success'), + 'data' => $this->model->getConfig(), + ]); + } + + /** + * 保存配置 + */ + public function save(): Response + { + $param = $this->request->param(); + $payload = [ + 'seo' => $param['seo'] ?? [], + 'header_nav' => $param['header_nav'] ?? [], + 'footer_nav' => $param['footer_nav'] ?? [], + 'site_config' => $param['site_config'] ?? [], + 'friendly_link' => $param['friendly_link'] ?? [], + 'banner' => $param['banner'] ?? [], + 'side' => $param['side'] ?? [], + ]; + + $config = $this->model->saveConfig($payload); + + return json([ + 'status' => 200, + 'msg' => lang_plugins('theme_configurator_save_success'), + 'data' => $config, + ]); + } +} diff --git a/plugins/addon/theme_configurator/controller/clientarea/ThemeController.php b/plugins/addon/theme_configurator/controller/clientarea/ThemeController.php new file mode 100644 index 0000000..fe97856 --- /dev/null +++ b/plugins/addon/theme_configurator/controller/clientarea/ThemeController.php @@ -0,0 +1,61 @@ +model = new ThemeConfigModel(); + } + + /** + * 返回给 BlackFruit-UI 的 /console/v1/common 同结构数据 + */ + public function config(): Response + { + $config = $this->model->getConfig(); + + $data = [ + 'enterprise_name' => $config['site_config']['enterprise_name'] ?? '', + 'enterprise_telephone' => $config['site_config']['enterprise_telephone'] ?? '', + 'enterprise_mailbox' => $config['site_config']['enterprise_mailbox'] ?? '', + 'enterprise_qrcode' => $config['site_config']['enterprise_qrcode'] ?? '', + 'official_website_logo' => $config['site_config']['official_website_logo'] ?? '', + 'icp_info' => $config['site_config']['icp_info'] ?? '', + 'icp_info_link' => $config['site_config']['icp_info_link'] ?? '', + 'public_security_network_preparation' => $config['site_config']['public_security_network_preparation'] ?? '', + 'public_security_network_preparation_link' => $config['site_config']['public_security_network_preparation_link'] ?? '', + 'telecom_appreciation' => $config['site_config']['telecom_appreciation'] ?? '', + 'copyright_info' => $config['site_config']['copyright_info'] ?? '', + 'terms_service_url' => $config['site_config']['terms_service_url'] ?? '', + 'terms_privacy_url' => $config['site_config']['terms_privacy_url'] ?? '', + 'cloud_product_link' => $config['site_config']['cloud_product_link'] ?? '', + 'dcim_product_link' => $config['site_config']['dcim_product_link'] ?? '', + 'honor' => $config['site_config']['honor'] ?? [], + 'partner' => $config['site_config']['partner'] ?? [], + 'friendly_link' => $config['friendly_link'] ?? [], + 'banner' => $config['banner'] ?? [], + 'header_nav' => $config['header_nav'] ?? [], + 'footer_nav' => $config['footer_nav'] ?? [], + 'side_floating_window' => $config['side'] ?? [], + 'feedback_type' => $config['site_config']['feedback_type'] ?? [], + ]; + + return json([ + 'status' => 200, + 'msg' => lang_plugins('theme_configurator_success'), + 'data' => $data, + ]); + } +} diff --git a/plugins/addon/theme_configurator/lang/zh-cn.php b/plugins/addon/theme_configurator/lang/zh-cn.php new file mode 100644 index 0000000..b480169 --- /dev/null +++ b/plugins/addon/theme_configurator/lang/zh-cn.php @@ -0,0 +1,10 @@ + '请求成功', + 'theme_configurator_save_success' => '主题配置保存成功', + 'theme_configurator_install_success' => '主题配置插件安装成功', + 'theme_configurator_uninstall_success' => '主题配置插件卸载成功', + 'nav_plugin_addon_theme_configurator' => '主题配置', + 'auth_plugin_addon_theme_configurator' => '主题控制', + 'auth_plugin_addon_theme_configurator_save' => '保存主题配置', +]; diff --git a/plugins/addon/theme_configurator/model/ThemeConfigModel.php b/plugins/addon/theme_configurator/model/ThemeConfigModel.php new file mode 100644 index 0000000..8afb156 --- /dev/null +++ b/plugins/addon/theme_configurator/model/ThemeConfigModel.php @@ -0,0 +1,93 @@ +table)->order('id', 'asc')->find(); + if (!$row) { + return $this->defaultConfig(); + } + + $config = json_decode($row['config'], true); + return is_array($config) ? $config : $this->defaultConfig(); + } + + /** + * 保存配置 + */ + public function saveConfig(array $config): array + { + $payload = [ + 'config' => json_encode($config, JSON_UNESCAPED_UNICODE), + 'update_time' => time(), + ]; + + $exists = Db::name($this->table)->order('id', 'asc')->find(); + if ($exists) { + Db::name($this->table)->where('id', $exists['id'])->update($payload); + } else { + Db::name($this->table)->insert($payload); + } + + return $config; + } + + /** + * 安装时写入默认配置 + */ + public function initConfig(): void + { + $exists = Db::name($this->table)->order('id', 'asc')->find(); + if (!$exists) { + $this->saveConfig($this->defaultConfig()); + } + } + + /** + * 默认结构 + */ + protected function defaultConfig(): array + { + return [ + 'seo' => [ + 'title' => 'BlackFruit-UI', + 'keywords' => '云服务器,主题云,BlackFruit', + 'description' => 'BlackFruit-UI 默认站点说明', + ], + 'header_nav' => [], + 'footer_nav' => [], + 'site_config' => [ + 'enterprise_name' => '主题云', + 'enterprise_telephone' => '400-000-0000', + 'enterprise_mailbox' => 'support@example.com', + 'enterprise_qrcode' => '/upload/qrcode.png', + 'official_website_logo' => '/upload/logo.png', + 'icp_info' => '京ICP备示例号', + 'icp_info_link' => 'https://beian.miit.gov.cn/#/Integrated/index', + 'public_security_network_preparation' => '京公网安备示例号', + 'public_security_network_preparation_link' => 'https://beian.mps.gov.cn/#/query/webSearch', + 'telecom_appreciation' => '增值电信业务经营许可证', + 'copyright_info' => '© ' . date('Y') . ' 主题云', + 'terms_service_url' => '/agreement/service.html', + 'terms_privacy_url' => '/agreement/privacy.html', + 'cloud_product_link' => '/cart/goods.htm?id=1', + 'dcim_product_link' => '/cart/goods.htm?id=2', + ], + 'friendly_link' => [], + 'banner' => [], + 'side' => [], + ]; + } +} diff --git a/plugins/addon/theme_configurator/route.php b/plugins/addon/theme_configurator/route.php new file mode 100644 index 0000000..c34f425 --- /dev/null +++ b/plugins/addon/theme_configurator/route.php @@ -0,0 +1,30 @@ +append([ + '_plugin' => 'theme_configurator', + '_controller' => 'theme', + '_action' => 'config', + ]); +})->middleware(\app\http\middleware\ParamFilter::class); + +// 后台配置接口 +Route::group(DIR_ADMIN . '/v1', function () { + Route::get('theme/config', "\\addon\\theme_configurator\\controller\\ThemeController@config") + ->append([ + '_plugin' => 'theme_configurator', + '_controller' => 'theme', + '_action' => 'config', + ]) + ->middleware(\app\http\middleware\CheckAdmin::class); + Route::post('theme/config', "\\addon\\theme_configurator\\controller\\ThemeController@save") + ->append([ + '_plugin' => 'theme_configurator', + '_controller' => 'theme', + '_action' => 'save', + ]) + ->middleware(\app\http\middleware\CheckAdmin::class); +})->middleware(\app\http\middleware\ParamFilter::class); diff --git a/plugins/addon/theme_configurator/sidebar.php b/plugins/addon/theme_configurator/sidebar.php new file mode 100644 index 0000000..9798ba4 --- /dev/null +++ b/plugins/addon/theme_configurator/sidebar.php @@ -0,0 +1,17 @@ + 'nav_plugin_addon_theme_configurator', + 'url' => '', + 'icon' => 'dashboard', + 'in' => '', + 'child' => [ + [ + 'name' => 'nav_plugin_addon_theme_configurator', + 'url' => 'index', + 'in' => '', + 'icon' => 'setting', + ], + ], + ], +]; diff --git a/plugins/addon/theme_configurator/sidebar_clientarea.php b/plugins/addon/theme_configurator/sidebar_clientarea.php new file mode 100644 index 0000000..f2e392c --- /dev/null +++ b/plugins/addon/theme_configurator/sidebar_clientarea.php @@ -0,0 +1,9 @@ + 'nav_plugin_addon_theme_configurator', + 'url' => 'theme_configurator', + 'icon' => 'setting', + 'child' => [], + ], +]; diff --git a/plugins/addon/theme_configurator/template/admin/index.php b/plugins/addon/theme_configurator/template/admin/index.php new file mode 100644 index 0000000..3bb840f --- /dev/null +++ b/plugins/addon/theme_configurator/template/admin/index.php @@ -0,0 +1,82 @@ + + + +
+ +此页面演示如何通过插件快速调整 BlackFruit-UI 的导航、页脚、SEO、轮播、侧边栏以及企业信息。直接编辑下方 JSON 提交即可。
+ + +