chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ywxapp\controller;
|
||||
|
||||
use think\facade\View;
|
||||
use ywxapp\model\Configure as ConfModel;
|
||||
use ywxapp\model\Links;
|
||||
use ywxapp\model\Notice;
|
||||
use ywxapp\model\Ad;
|
||||
use ywxapp\library\SkinOverlay;
|
||||
use ywxapp\library\SkinVariables;
|
||||
use ywxapp\library\TemplateManager;
|
||||
|
||||
/**
|
||||
* 前台控制器基类(统一核心前台与插件前台).
|
||||
*
|
||||
* - 核心前台(app\frontend\...):afterAuth 不启用后台 layout,渲染视图走应用默认视图目录。
|
||||
* - 插件前台(addon\*\...):视图根切到 addon/<name>/view/frontend/,
|
||||
* fetch 叠加皮肤覆盖层、配色变量与风格切换器。
|
||||
*
|
||||
* 公共逻辑(站点配置 / 友情链接 / user / success / error)统一落地于此,
|
||||
* 原 Frontend、AddonFrontend 已并入并删除。
|
||||
*/
|
||||
abstract class FrontendBase extends BaseController
|
||||
{
|
||||
/**
|
||||
* 视图类实例
|
||||
* @var \think\View
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* 前台默认放行登录:前台本质是「开放展示」,故默认不拦截(游客可访问)。
|
||||
* 需要登录 + 用户组权限的控制器(如插件用户中心、开发者中心)把本属性
|
||||
* 设为 [] 或具体 action 数组,即复用 BaseController 同一份 verifyAuth 守卫。
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedLogin = ['*'];
|
||||
|
||||
/**
|
||||
* 控制器初始化骨架.
|
||||
* @return void
|
||||
*/
|
||||
public function _initialize()
|
||||
{
|
||||
$this->view = $this->app->view;
|
||||
if ($this->isAddonContext()) {
|
||||
// 插件前台:视图根切到插件自身 view/frontend/
|
||||
$this->switchAddonViewPath();
|
||||
} else {
|
||||
// 核心前台(如 app/wxapp):统一走全局 layout 外壳,
|
||||
// 子模板写裸内容,由 common/layout.html 的 {__CONTENT__} 包裹。
|
||||
$this->view->config([
|
||||
'layout_on' => true,
|
||||
'layout_name' => 'common/layout',
|
||||
]);
|
||||
// 默认页面标题(子模板可通过 view() 的变量覆盖)
|
||||
$this->view->assign('title', 'YwxApp 框架展示');
|
||||
}
|
||||
|
||||
// 登录与用户组权限校验:默认放行(noNeedLogin=['*']),需登录的子类自行收窄。
|
||||
// verifyAuth 内部 tokenParse() 还原登录态并做权限校验,未登录整页 302 / AJAX 返 401,
|
||||
// 与后台、会员中心共用同一份 Auth::verifyAuth 逻辑。
|
||||
$this->auth->verifyAuth($this->noNeedLogin, $this->noNeedVerify);
|
||||
|
||||
if ($this->auth && $this->auth->isLogin) {
|
||||
$this->view->assign('member', $this->auth->info);
|
||||
}
|
||||
$siteConf = ConfModel::cache(true)->column('value', 'name');
|
||||
$this->view->assign('siteConf', $siteConf);
|
||||
$links = Links::cache(true)->where('status', 1)->order('sort desc')->select();
|
||||
$this->view->assign('links', $links);
|
||||
// 全站置顶公告条(方案 A):仅取当前生效 + 置顶 + 启用的公告
|
||||
$typeList = Notice::typeList();
|
||||
$topNotices = array_map(function ($n) use ($typeList) {
|
||||
$n['type_text'] = $typeList[$n['type']] ?? '公告';
|
||||
return $n;
|
||||
}, Notice::getActiveTopNotices());
|
||||
$this->view->assign('topNotices', $topNotices);
|
||||
// 全站广告位:按 position 分组的启用广告,模板可随时调用 {notempty name="adSlots.home_top"}
|
||||
$this->view->assign('adSlots', Ad::getSlots());
|
||||
// 注入站点信息(route_base / site.module / site.controller 等),供前端 route.js 使用
|
||||
$this->assignSite();
|
||||
// 前台主导航菜单(后台可配置,两级下拉),供 header.html 渲染
|
||||
$this->view->assign('navMenus', \app\backend\model\NavMenu::getNavTree());
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制器初始化 initialize(子类重写).
|
||||
* @return void
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 赋值到模板.
|
||||
*/
|
||||
public function assign($name, $value = null)
|
||||
{
|
||||
View::assign($name, $value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从控制器类名反推插件目录并切换视图根目录到 addon/<name>/view/frontend/.
|
||||
* @return void
|
||||
*/
|
||||
protected function switchAddonViewPath()
|
||||
{
|
||||
// 重构版 MultiApp 已将 appPath 设为 addon/<插件>/,直接拼视图目录,无需正则
|
||||
$this->view->config([
|
||||
'view_path' => $this->app->getAppPath() . 'view' . DIRECTORY_SEPARATOR
|
||||
. 'frontend' . DIRECTORY_SEPARATOR,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前插件名(从控制器命名空间反推 addon\<name>\...).
|
||||
* 由 appPath(addon/<插件>/)剥 rootPath 取首段,无需正则。
|
||||
* @return string
|
||||
*/
|
||||
protected function currentAddon(): string
|
||||
{
|
||||
$rel = trim(substr($this->app->getAppPath(), strlen($this->app->getRootPath())), DIRECTORY_SEPARATOR);
|
||||
$seg = explode(DIRECTORY_SEPARATOR, $rel);
|
||||
return $seg[0] === 'addon' && isset($seg[1]) ? $seg[1] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前控制器是否属于插件上下文(addon\ 命名空间).
|
||||
* @return bool
|
||||
*/
|
||||
protected function isAddonContext(): bool
|
||||
{
|
||||
return strpos($this->app->getNamespace(), 'addon\\') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户信息(前台会员).
|
||||
* @return mixed
|
||||
*/
|
||||
protected function user()
|
||||
{
|
||||
if ($this->auth && $this->auth->isLogin) {
|
||||
return $this->auth->info;
|
||||
}
|
||||
$this->result->error('获取用户信息失败!');
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染模板:插件前台叠加皮肤覆盖层 / 配色变量 / 风格切换器.
|
||||
*/
|
||||
protected function fetch($template = '', $vars = [], $replace = [], $config = [])
|
||||
{
|
||||
if ($this->isAddonContext()) {
|
||||
$addon = $this->currentAddon();
|
||||
$restore = null;
|
||||
if ($addon && $overlay = SkinOverlay::resolve($addon, 'frontend')) {
|
||||
// 把视图根目录切到「插件视图 + 皮肤覆盖」合并层,使 extend 的 layout 也走皮肤
|
||||
$restore = View::getConfig('view_path');
|
||||
View::config(['view_path' => $overlay]);
|
||||
}
|
||||
$result = View::fetch($template, $vars, $replace, $config);
|
||||
if ($restore !== null) {
|
||||
View::config(['view_path' => $restore]);
|
||||
}
|
||||
// 注入皮肤变量 CSS(Discuz 式配色层,无需改 HTML)
|
||||
$style = SkinVariables::styleTag($addon, 'frontend');
|
||||
if ($style !== '' && ($pos = stripos($result, '</head>')) !== false) {
|
||||
$result = substr($result, 0, $pos) . $style . "\n" . substr($result, $pos);
|
||||
} elseif ($style !== '') {
|
||||
$result = $style . $result;
|
||||
}
|
||||
// 注入前台风格切换器(界面设置开启 allow_member_select 时自动出现)
|
||||
$switcher = TemplateManager::switcherHtml($addon, 'frontend');
|
||||
if ($switcher !== '') {
|
||||
if (($pos = stripos($result, '</body>')) !== false) {
|
||||
$result = substr($result, 0, $pos) . $switcher . "\n" . substr($result, $pos);
|
||||
} else {
|
||||
$result .= $switcher;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
return View::fetch($template, $vars, $replace, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* success/error 已统一提升到 BaseController(success/error 代理 Result),此处不再重复定义。
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user