request->param('lang', ''); if (in_array($lang, ['zh-cn', 'en-us'], true)) { Lang::setLangSet($lang); Cookie::set('download_lang', $lang); } elseif ($cookie = Cookie::get('download_lang')) { Lang::setLangSet($cookie); } // 固定页面文案注入模板 $this->view->assign([ 'site_name' => lang('site_name'), 'all_category_text' => lang('all_category'), 'hot_text' => lang('hot'), 'latest_text' => lang('latest'), 'list_text' => lang('list'), 'detail_text' => lang('detail'), 'search_text' => lang('search'), 'search_placeholder' => lang('search_placeholder'), 'download_btn_text' => lang('download_btn'), ]); // 插件前台接入「全站动态布局」:视图只写正文,由核心 frontend 的 // common/layout.html 包裹全站 header / footer(与开发文档约定一致)。 // 注意:layout_name 必须带 .html 扩展名,否则 think-template 的 // parseTemplateFile() 会把无扩展名的绝对路径里的盘符冒号(D:)当成 // 模板分隔符替换成当前 viewPath,导致「模板文件不存在」。 $this->view->config([ 'layout_on' => true, 'layout_name' => $this->app->getRootPath() . 'app' . DIRECTORY_SEPARATOR . 'frontend' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'layout.html', ]); } /** * 读取本插件配置(运行时由 AppInit 注入 config('download')) */ protected function pluginConfig(): array { return Config::get('download', []); } /** * 首页:分类导航 + 热门/最新(按 config 开关) */ public function index() { $cfg = $this->pluginConfig(); $categories = Category::with(['resources' => function ($q) { $q->order('downloads', 'desc')->limit(8); }]) ->where('pid', 0) ->where('status', 1) ->order('sort', 'desc') ->select(); $hot = []; $news = []; if (!empty($cfg['show_hot'])) { $hot = Resource::scope('visible')->order('downloads', 'desc')->limit(10)->select(); } if (!empty($cfg['show_new'])) { $news = Resource::scope('visible')->order('id', 'desc')->limit(10)->select(); } $this->view->assign('categories', $categories); $this->view->assign('hot', $hot); $this->view->assign('news', $news); $this->view->assign('config', $cfg); return $this->view->fetch(); } /** * 分类列表页 */ public function list() { $cid = $this->request->param('cid/d', 0); $page = $this->request->param('page/d', 1); $cfg = $this->pluginConfig(); $size = (int)($cfg['page_size'] ?? 20); $cat = $cid ? Category::find($cid) : null; if ($cid && !$cat) { $this->error('分类不存在'); } $list = Resource::scope('visible'); if ($cid) { $list = $list->where('cid', $cid); } $list = $list->order('downloads', 'desc') ->paginate(['page' => $page, 'list_rows' => $size]); $this->view->assign('cat', $cat); $this->view->assign('list', $list); $this->view->assign('pager', $list->render()); $this->view->assign('config', $cfg); return $this->view->fetch(); } /** * 详情页 */ public function detail() { $id = $this->request->param('id/d', 0); $resource = Resource::scope('visible')->find($id); if (!$resource) { $this->error('资源不存在或已下架'); } // 查看计数(防刷交给 service 内的简单锁,此处仅点击+1) DownloadService::incClicks($id); $cat = $resource['cid'] ? Category::find($resource['cid']) : null; $this->view->assign('resource', $resource); $this->view->assign('cat', $cat); $this->view->assign('config', $this->pluginConfig()); return $this->view->fetch(); } /** * 搜索 */ public function search() { $q = $this->request->param('q', $this->request->param('kw', '')); $page = $this->request->param('page/d', 1); $cfg = $this->pluginConfig(); $size = (int)($cfg['page_size'] ?? 20); $list = Resource::scope('visible'); if ($q) { $list = $list->whereLike('title', "%{$q}%"); } $list = $list->order('downloads', 'desc') ->paginate(['page' => $page, 'list_rows' => $size]); $this->view->assign('keyword', $q); $this->view->assign('list', $list); $this->view->assign('pager', $list->render()); $this->view->assign('config', $cfg); return $this->view->fetch(); } /** * 触发下载:外链 302 跳转 / 本地文件流式输出 */ public function down() { $id = $this->request->param('id/d', 0); $ip = $this->request->ip(); try { $ret = DownloadService::dispatch($id, $ip); } catch (\think\Exception $e) { $this->error($e->getMessage()); return; } if ($ret['type'] === 'redirect') { return redirect($ret['url']); } // 本地文件流式输出(避免大文件占用内存) $path = $ret['path']; $name = ($ret['name'] ?? 'download') . '.' . pathinfo($path, PATHINFO_EXTENSION); return Response::create()->data(file_get_contents($path))->header([ 'Content-Type' => 'application/octet-stream', 'Content-Disposition' => 'attachment; filename="' . rawurlencode($name) . '"', 'Content-Length' => filesize($path), ]); } }