1. 安装程序
DTR自动收录导航 是纯 PHP 程序,不用 Composer、不用框架,上传后浏览器点一遍就装好。
1. 把整包上传到网站根目录(wwwroot 这一层),index.php 必须在根目录里
2. 浏览器打开 https://你的域名/install/ ,填数据库信息与管理员账号
3. 装完自动写入 config/install.lock,重复访问安装页会被拦住
4. config 与 storage 目录需要 PHP 可写(755 即可)
宝塔伪静态
宝塔面板 → 网站 → 对应站点 → 设置 → 「伪静态」,把下面三行整段粘进去保存。
只填这三行,不要自己写 server { } 外壳,面板会自动套:
location ~ ^/(app|config|database|storage(?!/uploads/))/ { deny all; }
location ~ ^/(templates/[^/]+/(views/|.*\.html$)|public/[^/]+/(data/|views/|[^/]+\.(php|json|txt|md|html)$)|storage/uploads/.*\.php$)|\.(sql|log|lock)$|/\.(htaccess|user\.ini)$ { deny all; }
if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s=$1 last; }
第一行挡程序目录(storage/uploads 图片目录除外),第二行挡模板内部文件、插件程序与数据、备份文件, 第三行才是伪静态。顺序不要调换;Apache / IIS 用包里的 .htaccess 与 web.config,不用管这里。
2. 填写授权码
程序装好后默认未激活,后台除了授权页以外会被锁定。授权服务器与通信密钥已经内置,站长只需要填一枚授权码。
- 打开授权管理登录主程序后台,左侧菜单点「授权管理」。页面会显示一串「本站指纹」(8 位),申领授权码时把「域名 + 这串指纹」一起发给老李。
- 粘贴授权码授权码一栏粘贴老李发的那串,形如 DTR-XXXXXXXX-XXXXXXXX-XXXXXXXX,第一段就是本站指纹,注意大小写与横线。
- 保存并校验点「保存并校验」。状态显示「授权有效」、方案与到期时间即为开通,不需要填授权服务器、密钥或绑定域名。
- 域名自动识别程序按当前访问域名自动校验;换域名或换服务器联系老李重新换绑,换绑会给出一枚新码,替换后点「立即联网检测」即可,不用重装。
- 离线宽限在线校验成功后结果缓存 12 小时;授权服务器暂时连不上时,站点在默认 7 天宽限期内照常运行,恢复后自动重验。
授权状态、到期时间、绑定域名、本站指纹、上次校验时间都在「授权管理」页显示,随时可以点「立即联网检测」强制刷新。
常见校验提示
| 提示 | 怎么处理 |
|---|---|
| 授权码不存在 | 授权码抄错(大小写、横线丢了),或这枚码已被删除。 |
| 授权码与当前域名不匹配 | 这串码是发给别的域名的(码里的指纹段和本站指纹不一样)。联系老李按本站域名重新换绑取一枚新码,替换后点「立即联网检测」。 |
| 该授权码已被停用 | 授权被临时停用或已吊销,请联系老李恢复。 |
| 授权已到期 | 续费时从原到期日顺延,续费后点一次「立即联网检测」。 |
| 无法连接授权服务器 | 网络或授权服务器暂时不可达,站点进入离线宽限;长时间不恢复再联系老李。 |
不要为了跳过校验去改授权文件:改坏之后换绑、续费都会失效,而且无法自动恢复。授权问题直接找老李处理最快。
3. 后台功能地图
后台是左侧菜单加多标签页,常用的收录、分类、文章、插件都在一个地方,不用记路径。
站点库 添加/编辑站点、网址别名、推荐位、上下架、批量操作
收录申请 待审列表、一键通过自动入站、驳回
分类与导航 站点分类、导航菜单、公告、友情链接
文章频道 文章分类、文章列表、富文本编辑
会员中心 注册用户、重置密码、禁用、删除
模板管理 模板启用与单独配置
插件管理 插件开关、设置、数据管理
站点设置 站点信息、SEO、截图 API、真实 IP 头、关于页、代码位
授权管理 只填授权码,保存并联网检测
几个容易忽略的设置:站点设置里的「网址后缀」对全站地址生效(默认 .html);「截图 API 前缀」用于站点详情页大图; 「真实访客 IP 头」只有套了 CDN 或反向代理才填,直接部署请留空。改完设置记得保存,再回前台刷新确认。
插件管理里每个插件都有「开关 / 设置 / 管理」三个按钮。自带的 TDK 获取插件就在这里开关, 抓取超时、是否覆盖已填内容、限流间隔都在它的「设置」里改,改完点保存即生效。
4. 开发新模板
一套模板就是 templates/ 下的一个文件夹,纯 PHP 写页面,没有模板标签、没有编译缓存。
做好后丢进目录,后台「模板管理」自动列出来,点「启用」就切换;删掉整个文件夹就能干净移除,不留残余。
4.1 目录结构
templates/你的模板名/
├─ Dtr.php 模板信息(后台模板列表读取)
├─ Config.php 模板配置默认值(后台改过的值存数据库)
├─ Form.html 后台「模板配置」表单
├─ pic.png 模板预览图
├─ views/ 前台视图
│ ├─ layout.php 公共外壳(有它就不用每页写头尾)
│ ├─ index.php sort.php site.php search.php pager.php
│ ├─ nav.php ranking.php ranking_body.php 404.php
│ ├─ apply.php daishen.php about.php tongji.php
│ ├─ wz_all.php wz_show.php wz_sort.php
│ └─ user.php login.php register.php
└─ public/pc/ 本模板自带的 css / js / img
最快上手方式:把 templates/default 整个复制一份改名,先改 Dtr.php 的
name 与 title、再换 public/pc/ 里的配色,前台就能跑;之后逐步替换页面。
缺哪个视图文件,引擎会自动回退到 default 的同名文件,所以可以只做首页、其余页面先沿用默认。
4.2 模板信息 Dtr.php
// Dtr.php —— 键名固定,值随便改;name 必须等于目录名
return [
'title' => '我的模板',
'name' => 'mytpl',
'author' => '张三',
'version' => '1.0.0',
'pic' => 'pic.png',
'link' => 'https://wpa.qq.com/msgrd?v=3&uin=1735737677&site=qq&menu=yes',
'set' => '1', // 1 = 允许后台「模板配置」
'desc' => '一句话介绍这套模板。',
];
后台模板列表靠这个文件识别模板:没有 Dtr.php 的目录不会出现在列表里,name 必须和目录名一致。
4.3 模板配置 Config.php + Form.html
// Config.php —— 键名自定义,视图里用 tplcfg('键名') 取
return [
'hero_title' => 'DTR 自动收录导航',
'hero_desc' => '专注优质站点收录,自动抓取、人工审核、快速上线',
'theme_main' => '#2fadf8',
'foot_link' => "首页,/\n关于,/about.html", // 多行值必须用双引号
// 开关型字段列在这里:未勾选存 0,勾选存 1
'_switch' => 'hero_open,show_tui,show_new,show_hot',
];
<!-- Form.html:字段名必须和 Config.php 的键一一对应 -->
<input class="input" name="hero_title" value="<?php echo e(tplcfg('hero_title')); ?>">
<label class="switch">
<input type="checkbox" name="hero_open" value="1" <?php echo tplcfg('hero_open') == '1' ? 'checked' : ''; ?>>
<i></i><span>显示首页横幅</span>
</label>
默认值写在 Config.php,站长在后台「模板配置」改过的值存进数据库,不会覆盖你的文件;
点「恢复默认」就删掉数据库里那份,重新读你的文件。视图里一律用 tplcfg('键名') 取值。
两个最容易踩的坑:① 多行默认值(比如页脚链接)必须用双引号写 "a\nb",单引号里的 '\n' 是两个字符;
② 勾选类字段要在数组里写一行 '_switch' => 'a,b,c' 把开关字段名列出来,没列出的字段按普通文本保存。
4.4 视图里能用的函数与变量
<?php /* views/ 里能直接用的函数 */ ?>
<?= e(tplcfg('hero_title')) ?> 模板配置(后台改过就用改后的)
<?= e(dtr('name')) ?> 站点名称
<?= tpl_url('css/style.css') ?> 本模板静态资源 → templates/当前模板/public/pc/css/style.css
<?= url('index') ?> 首页地址(自动带伪静态后缀与子目录)
<?= url('sort', ['alias' => 'gongju']) ?> 分类地址 → /list/gongju.html
<?= e(cut($site['description'], 80)) ?> 截断输出
<?= ad(1) ?> 广告位 1(后台「系统设置 → 广告位」里贴代码)
<?php /* 控制器传什么变量,视图里就有什么 */ ?>
index.php:$sorts / $recommend / $latest / $topView / $recent / $notice / $links / $articles
sort.php:$sort / $sites / $total / $pages / $cur
site.php:$site / $sort / $domain / $related
wz_show.php:$article / $sortwz
数据由控制器传进来,模板不做查询;静态资源一定要用 tpl_url() 输出,这样才会指向本模板自己的目录,
写死 /public/... 会串到插件目录去。所有输出用户内容的地方都要用 e() 转义。
4.5 给插件留钩子
插件靠模板里的钩子把入口挂进页面(比如插件在页脚挂一个入口链接)。这几个钩子建议照着 default 全部保留, 新模板少写一个,对应插件在前台就没有入口:
<?php \DTR\Core\Hooks::fire('front_head', ['page' => $page]); ?> <!-- </head> 前:插件插 css/js -->
<?php \DTR\Core\Hooks::fire('front_nav_extra', ['navs' => $navs]); ?> 导航条末尾
<?php \DTR\Core\Hooks::fire('front_footer_extra'); ?> 页脚链接区(插件入口挂这里)
<?php \DTR\Core\Hooks::fire('front_body_end', ['page' => $page]); ?> </body> 前:统计脚本等
# 内容插槽(模板里留位置,插件或广告往里放内容)
front_slot_head front_slot_home front_slot_side front_slot_detail
5. 开发新插件
一个插件就是 public/ 下的一个文件夹。放进目录后台「插件管理」自动列出,
列表上有「开启 / 关闭」和「设置」按钮。删掉整个文件夹即卸载,不需要安装、不需要卸载动作;
只有自己在 plugin.json 里声明过数据表的插件,删除时才会多一个「删数据表」勾选。
5.1 目录结构
public/你的插件目录/
├─ plugin.json 插件信息
├─ 入口文件.php 插件主类,继承 \DTR\Core\Plugin
├─ views/ 插件前台页面(renderFront 会套当前模板头尾)
├─ public/ 插件自带 css / js / img(别引用模板资源)
├─ data/ 插件自己的数据(示例插件用 json 存这里)
└─ config.json 开关与设置,后台开启/关闭与保存时由框架读写
5.2 插件信息 plugin.json
// plugin.json
{
"name": "demo",
"title": "示例插件",
"version": "1.0.0",
"author": "老李",
"desc": "演示用:后台一键开关与设置,前台一个提交页。",
"entry": "demo.php",
"class": "DTR\\Plugins\\Demo",
"has_config": 1,
"tables": ["demo_log"]
}
// entry = 入口文件名;class = 该文件里的完整类名
// has_config = 1 时后台插件列表出现「设置」按钮
// tables = 这个插件自己建的数据表(不写 dtr_ 前缀),表名必须以插件目录名开头
5.3 主类写法(下面是一个可直接照抄的例子)
<?php
namespace DTR\Plugins;
use DTR\Core\Plugin;
class Demo extends Plugin
{
/** 必须实现:挂动作钩子(插件开启时才会被调用) */
public function register(): void
{
\DTR\Core\Hooks::listen('front_footer_extra', function () {
if ($this->config('enabled', 0)) {
echo '<a href="' . site_url('/demo.html') . '">示例页面</a>';
}
});
}
/** 前台路由:[正则, 回调],正则是不含域名的路径 */
public function routes(): array
{
return [
['@^/demo\.html$@', function () { $this->renderFront('index'); }],
['@^/demo/submit$@', function () { /* 处理提交 */ }],
];
}
/** 后台「设置」弹窗表单(返回 HTML) */
public function adminForm(): ?string
{
return '<label><input type="checkbox" name="enabled" value="1"> 开启</label>'
. '<label><input type="checkbox" name="need_audit" value="1"> 提交需审核</label>';
}
/** 后台「设置」保存:字段全在 $post 里,自己写回 config.json */
public function adminSave(array $post): void
{
$this->saveConfig([
'enabled' => !empty($post['enabled']) ? 1 : 0,
'need_audit' => !empty($post['need_audit']) ? 1 : 0,
]);
}
}
基类 \DTR\Core\Plugin 提供的可覆盖方法与工具:
| 方法 | 说明 |
|---|---|
| register() | 必须实现。在这里 Hooks::listen() 挂动作;插件关闭时框架不会调用它。 |
| routes() | 返回 [[正则, 回调], ...],正则是去掉域名的路径,在所有内置路由之后、404 之前匹配。 |
| renderFront($view, $data) | 渲染插件自己的 views/ 页面,自动套当前模板的页头页尾。 |
| adminForm() | 返回 HTML,显示在后台「设置」弹窗里;has_config 为 1 才有这个按钮。 |
| adminSave($post) | 「设置」弹窗保存时进来,自己决定把哪些字段写回 config.json。 |
| adminAction($action) | 自定义后台管理页,访问 /admin/plugin/run/目录名/动作 时进来。 |
| config('键', 默认值) | 读 config.json;开关字段固定叫 enabled,由后台的开启/关闭按钮维护。 |
| saveConfig(['a' => 1]) | 合并写回 config.json,框架也会用它记住开启状态。 |
5.4 三条硬规矩
① 自包含:css / js / 图片 / 数据全放自己目录内,用 $this->asset('css/x.css') 这类方法引用,
绝不引用模板目录里的资源,否则换模板就坏。
② 写操作带令牌:后台管理页里每个表单都要输出 csrf_field(),后台对所有 POST 统一校验,漏了会提示「表单已过期」。
③ 关闭也要留地址:插件关闭时前台访问它的地址,框架会给出「已关闭」的中文提示页而不是空白 404,
所以路由里不要再自己判断开关后 exit 空白。
数据建议存自己的 data/*.json(上面这个示例插件就是这么存的),需要建表时再走数据库;
插件不要写 install 脚本,第一次访问时懒初始化最稳。
要建表的插件请在 plugin.json 里用 tables 声明表名:表名不带数据库前缀
(建表 SQL 里写 {{demo_log}},框架自动补成 dtr_demo_log),而且必须以插件目录名开头,
不合规的声明会被直接忽略。声明过的插件在后台删除时,卡片上会出现「删数据表」勾选,默认不勾:
不勾只删目录、表和数据留在库里,勾了才 DROP TABLE;没声明 tables 的插件删除时只删目录。
模板和插件做出来想在自家站点试效果,直接把文件夹丢进 templates/ 或 public/ 就能看见,
不用改任何程序代码。另有问题见 常见问题,
或直接找老李(QQ 310212367,群 812890793)。