简介:这是一份可直接部署的校园表白墙网站源码包,适合个人站长、学生或网页开发爱好者快速搭建表白网页或校园互动社区。源码包采用无数据库设计,上传至网站根目录并绑定域名即可运行,后台管理入口及默认账号信息已提供,安装门槛很低。压缩包共包含639个文件,整体仅16.65MB,以PHP后端、HTML/CSS/JS前端及各类静态资源为主:54个PHP实现动态功能,49个CSS和96个JS负责页面样式与交互逻辑,PNG/JPG图片构成主要视觉素材,结构清晰便于二次开发。目前已有1130人学习下载,值得作为练手项目参考。你可借此快速搭建一个真实可用的表白墙网站,也能学习无数据库场景下的PHP建站思路,稍作改造后即可变成校园留言墙、节日祝福页或送给恋人的惊喜礼物。
1. 静态表白墙源码的定位
拿到一份校园表白墙源码,先别急着上传解压。这套源码最特别的地方是「无数据库上传就能用」,但访客提交的每一句话到底写在哪里,取决于后台 /admin 的实现方式。很多从网盘下载的版本,入口页面是 html,样式表是 amazeui 和 bootstrap 的组合,后台用一个固定账号密码登录。它适合不想折腾数据库的个人站长、毕设演示、或者给对象做个小站,解决的核心问题不是高并发,而是「一个页面、一套后台、几分钟跑起来」的轻量表白场景。正因为它没有数据库,很多人第一步就卡在不知道数据怎么存、后台改了为什么前台不显示这类问题上。
2. 入口页面与静态资源加载链:html、css、js 如何串起来
2.1 入口文件与默认文档
解压后,目录里一般能看到 web.config、style.css、amazeui.min.css、bootstrap.css、fonts.css、buttons.css 以及后台 admin 目录。以我拆过的同类源码为例,整理出来的典型结构如下:
├─ index.html # 前台入口,也可以叫 index.php ├─ web.config # IIS 默认文档 / 重写规则 ├─ css/ │ ├─ style.css # 自定义主样式 │ ├─ amazeui.min.css # 移动端 UI 框架 │ ├─ bootstrap.css # 栅格与基础组件 │ ├─ bootstrap.min.css │ └─ buttons.css # 按钮样式 ├─ fonts/ # 图标字体 ├─ js/ # 前台交互脚本 ├─ data/ # 无数据库版的留言存储目录 └─ admin/ # 后台管理这里要先说清楚一个容易踩的坑:浏览器输入域名后先找哪个文件,由服务器决定。如果你跑在 Windows 的 IIS 上,web.config 里的 defaultDocument 会决定先加载 index.html 还是 index.php;如果跑在 Nginx 上,默认首页由 index 指令控制。无论哪个,入口文件里最终要出现的是<!doctype html>开头的完整 HTML 文档,搜索引擎收录时也以这个文档的 title、description、keywords 为准。所以你想改站点标题,第一步不是改后台,而是改入口文件的 head 区。
2.2 样式表的分工与取舍
一个包里同时出现 amazeui、bootstrap、buttons、fonts,看起来很多,实际各自负责的范围并不重叠。
| 文件 | 作用 | 什么时候需要 |
|---|---|---|
| style.css | 覆盖框架默认样式的自定义层 | 必留,改主题色和间距都在这 |
| amazeui.min.css | 移动端组件、表单、按钮、弹层 | 前台卡片和表单依赖它 |
| bootstrap.css | 栅格布局、基础排版 | 做响应式布局时用 |
| bootstrap.min.css | bootstrap 的压缩版 | 线上用 min 版,本地调试用未压缩版 |
| buttons.css | 按钮配色和 hover 动效 | 只有按钮区用到 |
| fonts.css | 图标字体与特殊字符 | 有 icon 字体时保留 |
如果你发现页面样式错乱,先看引入顺序。通用做法是 bootstrap 在最低层、amazeui 在其上、style.css 必须放在最后,否则你写的覆盖规则会被框架的同名类名压住。比较常见的问题是把 bootstrap.css 和 bootstrap.min.css 一起引入,浏览器虽然只执行后者,但多一次请求和潜在冲突,线上只保留一个即可。
2.3 从<!doctype html>开始拆入口结构
拿一份精简后的入口页面举例:
<!doctype html> <html lang="zh-cn"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="keywords" content="校园表白墙, 表白网页, html表白网站"> <title>校园表白墙 - 大胆说出爱</title> <link rel="stylesheet" href="css/bootstrap.min.css"> <link rel="stylesheet" href="css/amazeui.min.css"> <link rel="stylesheet" href="css/style.css"> </head> <body> <div id="wall-app"></div> <script src="js/wall-data.js"></script> <script src="js/main.js"></script> </body> </html>这段代码里的lang="zh-cn"告诉浏览器和搜索引擎当前页面语言是简体中文,对 SEO 有帮助;meta charset="utf-8"决定中文是否乱码,所有表白内容只要经过后台写入,都必须保持 utf-8 编码;meta name="viewport"是手机端适配的关键,没有它,iPhone 和安卓上页面会被缩放得很小。CSS 放在 head 里是为了让页面加载时有基本样式,js/main.js放在 body 底部是为了不阻塞首屏渲染。如果你从下载包里拿到的入口文件没有 viewport,直接补上这行,移动端显示立刻正常。
2.4 无数据库下页面数据从哪来
前面已经出现 data 目录,这是无数据库方案的核心。常见做法有两类:一是后端用 PHP 把留言追加写入一个 JSON 文件,二是纯前端把留言存 localStorage。区分方法很简单:清空浏览器缓存后刷新,如果之前发过的留言还在,说明走了服务端文件存储;如果只剩默认示例数据,说明是 localStorage 方案。后面章节以更常见的 JSON 文件存储为例展开,因为这样别人用不同手机访问都能看到同一份留言,localStorage 只适合自己本地开着玩。
3. 表白数据写入与读取:无数据库方案的实现细节
3.1 为什么无数据库方案够用
校园表白墙的本质是低频写入、高频读取。一个班级或一个学院一天能提交几十条已经很多,这种压力下,MySQL 反而成了负担:要创建库表、要维护账号、要在虚拟主机上折腾权限。JSON 文件方案把整张表变成一个 messages.json,前台用 fetch 读取,后台用 file_put_contents 追加,部署时只要保证 data 目录可写。
| 对比项 | MySQL 方案 | JSON 文件方案 |
|---|---|---|
| 部署依赖 | 需要数据库服务 | 仅需 PHP + 文件权限 |
| 备份 | mysqldump | 直接下载 data 目录 |
| 并发能力 | 高 | 低并发够用,几百人同时提交会丢数据 |
| 适合场景 | 正式运营、多端读写 | 校园表白、毕业留念、个人小站 |
所以「无数据库上传就能用」不是功能阉割,而是在这个场景里性价比最高的选择。要注意的是,文件方案扛不住刷接口,如果源码直接暴露了写入接口,恶意脚本可以一秒写几千条。后面章节会讲加一道简单校验来兜底。
3.2 留言数据长什么样
无数据库后台写入时,数据结构一般长这样:
[ { "nickname": "来自计算机学院的小张", "content": "图书馆三楼靠窗的位置,我注意你一个月了。", "time": "2025-01-01 20:18:00", "status": 1 }, { "nickname": "匿名", "content": "祝考研的学姐一切顺利。", "time": "2025-01-02 09:30:00", "status": 2 } ]数组里的每条记录包含昵称、内容、时间和状态字段。status 是后台控制显隐的关键:1 表示正常展示,2 表示待审核或已隐藏。前台读取时根据 status 过滤,后台编辑时只改这个值,不需要删数据,避免手滑把某条表白直接弄丢。字段名不一定是这两个,也可能是 name、msg、addtime,但作用完全一致。你先打开 data 下的 JSON 文件确认真实字段,再决定改前台渲染代码,否则会出现内容读到了但页面显示空白。
3.3 后台写入的 PHP 实现与参数细节
下面这段逻辑是我在同类源码里常用的保存方式,可以直接替换或对照理解原包里的保存脚本:
<?php // save_msg.php $nickname = trim($_POST['nickname'] ?? '匿名'); $content = trim($_POST['content'] ?? ''); if ($content === '') { exit(json_encode(['code' => 0, 'msg' => '内容不能为空'])); } $nickname = mb_substr($nickname, 0, 12); $content = mb_substr($content, 0, 200); $file = __DIR__ . '/data/messages.json'; $list = file_exists($file) ? json_decode(file_get_contents($file), true) : []; $list[] = [ 'nickname' => $nickname, 'content' => $content, 'time' => date('Y-m-d H:i:s'), 'status' => 1, ]; file_put_contents($file, json_encode($list, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), LOCK_EX); echo json_encode(['code' => 1, 'msg' => 'ok']);这段代码核心是三步:取参数、截断长度、追加写入。mb_substr按字符截断而不是按字节,避免把中文昵称切断成乱码;date('Y-m-d H:i:s')生成的是服务器本地时间,如果服务器时区不对,记得在文件头部加date_default_timezone_set('Asia/Shanghai');file_put_contents的LOCK_EX参数会在写入时加独占锁,阻止两个请求同时写坏文件,这是低并发方案里成本最低的防冲突手段。JSON_UNESCAPED_UNICODE保证中文在 JSON 里可读,JSON_PRETTY_PRINT让文件在编辑器里按行展示,排错时一眼能看出格式问题。注意保存路径要真实存在,如果没有 data 目录,file_put_contents不会自动建目录,页面会报 500。
3.4 前台读取与防 XSS 渲染
对应前台用 fetch 拉 JSON 再渲染成卡片:
fetch('data/messages.json', { cache: 'no-store' }) .then((res) => res.json()) .then((list) => { const box = document.getElementById('wall-app'); const valid = (list || []).filter((item) => item.status === 1); box.innerHTML = valid.map((item) => ` <div class="wall-card"> <p class="wall-nick">${escapeHtml(item.nickname)}</p> <p class="wall-text">${escapeHtml(item.content)}</p> <span class="wall-time">${escapeHtml(item.time)}</span> </div> `).join(''); function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str == null ? '' : String(str); return div.innerHTML; } }) .catch((err) => console.error('留言加载失败:', err));这里的cache: 'no-store'很关键,它告诉浏览器每次刷新都要重新请求 JSON,否则你在后台审核通过一条留言后,前台可能还显示旧文件。filter((item) => item.status === 1)对应前面说的状态字段,只有正常展示的留言进 DOM。escapeHtml用 textContent 再取 innerHTML 的方式转义掉<script>和标签符号,防止有人往昵称里塞一段 JS 造成存储型 XSS。这个细节很多静态源码都没做,上线前务必保留。
4. 后台 /admin 登录与内容管理流程
4.1 登录页的实现与判断方法
演示站的后台地址是 /admin,默认账号密码都是 ygwzjs.cn。网上流传的版本里,登录校验通常分两种:一种是纯前端 JS 校验,账号密码写死在 admin/login.js 里;另一种是 PHP 会话校验,登录成功后写入 session。区别在于前者直接打开源码就能看到密码,后者需要知道服务端逻辑。拿到源码先做一件事:用浏览器无痕窗口访问 /admin/login.js,看是否存在账号相关的字符串。如果有明文账号,第一件事就是改掉,防止别人用默认密码登录你的后台改页面。
4.2 后台操作步骤
以实际使用流程来看,管理员的日常操作集中在几个页面里:
- 访问 http(s)://你的域名/admin,进入登录页。
- 输入账号 ygwzjs.cn 和密码 ygwzjs.cn,点击登录。
- 进入表白列表,列表通常按时间倒序展示所有留言。
- 对需要隐藏或删除的记录操作,常见按钮有「隐藏」「通过」「删除」「置顶」。
- 有些版本支持直接编辑留言内容,注意编码要选 utf-8,否则中文会变成乱码。
这里的「隐藏」不是删掉记录,而是把 status 从 1 改成 2,前台渲染时自然过滤掉。如果是纯前端版本,这些操作直接修改 localStorage,换浏览器就看不到;如果是文件版本,修改的是 data 目录下的 messages.json。建议先发一条测试留言,看它落在哪里,再决定用哪种方式备份。不同版本后台按钮的叫法不一样,但对应动作就四种:
| 按钮 | 实际效果 | 对应字段变化 |
|---|---|---|
| 隐藏 | 前台不再显示,数据保留 | status: 1 -> 2 |
| 通过 | 恢复显示 | status: 2 -> 1 |
| 删除 | 从数据文件中移除 | 移除整条记录 |
| 置顶 | 排到列表最前 | 增加或修改排序值 |
4.3 后台登录校验收紧:从明文比对到 session
容易被人忽略的是,默认源码里的登录可能只是一个 if 判断,比如:
<?php // admin/login_check.php $user = $_POST['user'] ?? ''; $pass = $_POST['pass'] ?? ''; $okUser = 'ygwzjs.cn'; $okPass = 'ygwzjs.cn'; if ($user === $okUser && $pass === $okPass) { session_start(); $_SESSION['admin'] = true; header('Location: index.php'); } else { echo '账号或密码错误'; }这段逻辑能跑,但存在几个问题:账号密码写死在 PHP 里,一旦源码泄露就形同虚设;没有验证码,自动化工具可以暴力尝试;session 有效期没有限制,登录后长时间不关浏览器就一直有效。更稳的做法是把账号密码放到后台目录外的 config.php 里,并且在入口处加if (empty($_SESSION['admin'])) exit('请先登录');,这样前台用户无法直接访问 admin 目录下的页面文件。
4.4 目录与文件权限设置
后台能登录成功但保存失败,大多数情况是文件写权限没开。Linux 服务器上执行:
chown -R www-data:www-data /var/www/html chmod -R 755 /var/www/html chmod -R 777 /var/www/html/data第一条把站点目录属主改成 Nginx 或 PHP-FPM 的运行用户,第二条让目录和文件可读可执行,第三条只放开 data 目录的写权限。不要图省事直接chmod 777整个站点,否则任何脚本都能往你的网站目录里写入木马。Windows 的 IIS 下则要右键 data 目录,在安全选项卡里给 IUSR 或 IIS_IUSRS 添加修改权限。权限设置完,重新提交一条留言并查看 messages.json 的时间戳,如果时间变化就说明写入链路通了。
5. 部署到服务器:web.config、Nginx 与上线检查
5.1 上传文件与目录校验
不管用宝塔、FTP 还是命令行,上传前先在本地把压缩包解压,确认入口文件在根目录而不是嵌套一层文件夹。常见错误是把源码上传成了yourdomain.com/表白墙源码/index.html,访问网站变成目录列表。命令行上传推荐用 rsync,只同步变化文件:
rsync -avz --exclude='*.zip' ./biao-bai/ root@你的服务器IP:/var/www/html/这条命令的作用是把本地biao-bai/目录内容同步到服务器站点根目录。-a保留文件权限和时间戳,-v显示过程,-z传输前压缩。排除掉 zip 包是为了避免压缩包被人直接下载。同步完成后,用ls -l /var/www/html/index.html确认入口文件存在,再继续配置服务器。
5.2 web.config 与 IIS 默认文档
源码自带 web.config 是 IIS 下的配置文件。它最常见的用途是指定默认文档,因为静态源码入口可能是 index.html,而 IIS 默认优先找 index.aspx:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <defaultDocument> <files> <clear /> <add value="index.html" /> <add value="index.php" /> </files> </defaultDocument> </system.webServer> </configuration>clear清掉系统自带的默认文档列表,避免站点根目录同时存在 index.html 和 index.php 时选错。add value按从上到下的顺序匹配,排在前面的先被请求。如果你在 IIS 下打开页面返回 403.14,八成就是默认文档没匹配上,检查这里即可。另外有些版本的 web.config 里会带 rewrite 规则,用于去掉 URL 后缀,不要随意删除,否则后台链接会 404。
5.3 Nginx 站点配置
部署到 Nginx 时,web.config 不生效,需要在 server 块里写对应配置:
server { listen 80; server_name biaobai.example.com; root /var/www/html; index index.html index.php; location / { try_files $uri $uri/ =404; } location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/run/php/php8.1-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }root指向站点根目录,index决定访问根路径时加载哪个文件,顺序很重要:如果源码入口是 index.html,就把它放前面。try_files先找真实文件再找目录,都找不到返回 404,避免访问不存在的路径时泄露目录列表。location ~ \.php$是给 PHP 文件用的,如果你的版本是纯 HTML,这段可以不要;但如果后台是 PHP 实现的却没有这段配置,登录时就会直接下载 .php 文件而不是执行。
5.4 绑定域名、HTTPS 与缓存验证
域名解析生效后,先访问 http://域名 确认首页能开,再申请证书。用 certbot 的话一步到位:
certbot --nginx -d biaobai.example.com -d www.biaobai.example.com申请成功后,用 curl -I 检查返回头:
curl -I https://biaobai.example.com重点看两处:HTTP/2 200表示协议正常,strict-transport-security出现表示 HSTS 已开。如果首页能看到但 CSS 全乱,打开浏览器开发者工具看 Network 面板,404 的文件通常是 css 或 fonts 路径问题,因为子目录页面的相对路径写错导致加载不到根目录样式。这种情况把页面里的href="css/style.css"改成href="/css/style.css"即可。
6. 换肤、敏感词过滤与返回顶部:常用改造技巧
6.1 用 CSS 变量快速换肤
很多表白墙源码把主色写死成粉色系,改色要翻遍整个 style.css。做成 CSS 变量后,换主题只需要改一处:
:root { --primary: #ff6b81; --bg: #fdf6f0; --card-bg: #ffffff; } body { background: var(--bg); } .wall-card { background: var(--card-bg); border: 1px solid var(--primary); }当 style.css 里框架类名已经定义过背景或边框时,var()写法要放在 style.css 末尾,否则会被 amazeui 的同名类覆盖。想换成科技蓝,把--primary改成#4a90d9,--bg改成#f0f4fb即可。
6.2 前端敏感词过滤与提交拦截
静态源码没有后端内容审核,但可以在提交动作上做一道前端拦截:
const sensitiveWords = ['代做', '加微信']; function checkContent(text) { return sensitiveWords.some((word) => text.includes(word)); }在表单提交的 click 事件里先执行checkContent(value),命中就 return false 并提示用户修改。some的作用是遍历敏感词数组,只要有一个词在内容里出现就返回 true,性能开销可忽略。这道防线只对普通访客有效,接口层仍要在 PHP 保存脚本里用同样的数组再判断一次,否则 curl 直接 POST 就能绕过。
6.3 返回顶部按钮与移动端验证
列表页一长,返回顶部按钮比手动滑动更省事:
const backTop = document.getElementById('backTop'); window.addEventListener('scroll', function () { backTop.style.display = window.scrollY > 300 ? 'flex' : 'none'; }, { passive: true }); backTop.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); });window.scrollY > 300是按钮出现的滚动阈值,值越大越不容易打扰首屏;{ passive: true }告诉浏览器不调用 preventDefault,滚动监听不会拖慢页面。改造完按这个顺序验证:先在 PC 端改 CSS 变量看整站变色,再提交一条包含敏感词的测试内容看是否被拦截,最后滚动 500 像素确认按钮出现并平滑回顶。前端改造不涉及数据迁移,唯一要注意的是别把 style.css 里原有类名删了。
本文还有配套的精品资源,点击获取