feat: 补齐默认头像资源,修复「删除头像」静默失效
问题:添加观影人页面的头像预览指向 /assets/default-avatar.png,但仓库里
从来没有这个文件 → 该预览一直是坏图;同时未上传头像时会按姓名首字母生成
彩色 SVG 头像,导致「没有头像」这一种状态有两种表现,行为不统一。
改动
- assets/default-avatar.png(新增):200x200 中性灰人形剪影,随仓库分发;
生成脚本见 _tooling/make_default_avatar.py(Pillow,4 倍超采样)。
- lib/helpers.php:新增 defaultAvatarUrl();getAvatarUrl() 在未上传头像时
一律返回默认头像(不再生成首字母头像);资源缺失时退化为内联 SVG 剪影,
彻底消除「兜底图本身就是坏图」这类问题。
- persons.php:
* 后端开始处理 delete_avatar —— 此前前端设了标记、后端从未读取,
点「删除」再保存头像原样保留,是个静默失效的按钮。
* 上传新头像优先于删除标记,避免「先点删除、后又重新选图」时上传被忽略。
* removeAvatar() 复用同一个隐藏域,重复点击不再叠加多个同名 input。
* 表单预览统一走 getAvatarUrl()(顺带修掉头像键存在但文件丢失时仍渲染坏图)。
- README.md:更新头像兜底说明、目录树与「已知限制」。
验证
- storage_test.py 新增第 [21] 节 21 项断言(资源合法性、HTTP 可访问、四个分支、
资源缺失降级、页面渲染、删除头像端到端),合计 196 项全绿。
- 47 个端点的 A/B 回归:剔除「预期的头像地址替换」与「persons.php 内联 JS 重写」
后无任何其它差异(0 处未解释变更)。
- 第 10 批部署已上线:线上 assets/default-avatar.png 的 sha256 与本地逐字节一致,
全站无 PHP 诊断泄漏,数据行数不变(movies 219 / persons 17 / viewers 449)。
This commit is contained in:
+30
-7
@@ -135,17 +135,40 @@ function posterUrl($path) {
|
||||
}
|
||||
|
||||
// ===== 头像处理 =====
|
||||
|
||||
// 默认头像 URL:未上传头像的人统一使用它。
|
||||
// 资源随仓库分发(assets/default-avatar.png)。
|
||||
// 若该文件缺失——比如只部署了 PHP 而漏了 assets/——则退化为内联 SVG 剪影,
|
||||
// 避免出现「兜底图本身就是坏图」:这正是本函数此前的问题(返回了一个并不存在的路径)。
|
||||
// 注意:这里用 is_file() 探测,路径在站点目录内,不会像站外路径那样触碰 open_basedir 限制。
|
||||
function defaultAvatarUrl() {
|
||||
static $url = null;
|
||||
if ($url !== null) {
|
||||
return $url;
|
||||
}
|
||||
$root = defined('ROOT_DIR') ? ROOT_DIR : dirname(__DIR__);
|
||||
if (is_file($root . '/assets/default-avatar.png')) {
|
||||
$url = '/assets/default-avatar.png';
|
||||
return $url;
|
||||
}
|
||||
// 几何比例与 assets/default-avatar.png 保持一致,两种情形视觉无差别
|
||||
$svg = "<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200' viewBox='0 0 200 200'>"
|
||||
. "<rect width='200' height='200' fill='#e9edf3'/>"
|
||||
. "<circle cx='100' cy='74' r='33' fill='#7f8896'/>"
|
||||
. "<ellipse cx='100' cy='200' rx='74' ry='93' fill='#7f8896'/>"
|
||||
. "</svg>";
|
||||
$url = 'data:image/svg+xml,' . rawurlencode($svg);
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 头像 URL:已上传 → 存储层 URL;未上传 → 统一的默认头像。
|
||||
// 说明:$name 是历史遗留参数(早期按姓名首字母生成彩色 SVG 头像),
|
||||
// 现在一律使用默认头像,参数仅保留以兼容既有调用点。
|
||||
function getAvatarUrl($avatar_path, $name = '') {
|
||||
if (storageExists($avatar_path)) {
|
||||
return storageUrl($avatar_path);
|
||||
}
|
||||
if (!empty($name)) {
|
||||
$letter = mb_substr($name, 0, 1, 'UTF-8');
|
||||
$colors = ['#e74c3c','#e67e22','#f1c40f','#2ecc71','#3498db','#9b59b6','#1abc9c','#e84393','#00b894','#0984e3'];
|
||||
$color = $colors[abs(crc32($name)) % count($colors)];
|
||||
return "data:image/svg+xml," . urlencode("<svg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'><rect width='40' height='40' fill='{$color}'/><text x='20' y='28' text-anchor='middle' font-size='20' fill='#fff' font-family='sans-serif' font-weight='bold'>{$letter}</text></svg>");
|
||||
}
|
||||
return '/assets/default-avatar.png';
|
||||
return defaultAvatarUrl();
|
||||
}
|
||||
|
||||
// ===== 保存 Base64 图片 =====
|
||||
|
||||
Reference in New Issue
Block a user