fix: gitignore 排除了 PNG 资源导致构建失败
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m12s

- .gitignore 中 *.png 规则误忽略了 web/src/assets/ 和 web/public/ 下的 logo/favicon
- 添加例外规则允许 web 资源目录的 PNG 文件
- 补提交 logo_32/128/512.png + favicon.png
- 审计日志变更详情优化:字段名中文化 + 布尔值显示优化 + 空值兜底

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
seaislee1209 2026-03-16 05:49:21 +08:00
parent e5273540e9
commit b78a220082
6 changed files with 54 additions and 37 deletions

3
.gitignore vendored
View File

@ -31,6 +31,9 @@ test-screenshots/
# === Screenshots & prototype images ===
*.png
# Allow web assets and public PNGs
!web/src/assets/*.png
!web/public/*.png
# === Environment ===
.env

BIN
web/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
web/src/assets/logo_128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
web/src/assets/logo_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
web/src/assets/logo_512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

View File

@ -22,48 +22,62 @@ const ACTION_OPTIONS = [
{ label: '切换成员状态', value: 'member_status_toggle' },
];
const FIELD_LABELS: Record<string, string> = {
default_daily_seconds_limit: '每日限额',
default_monthly_seconds_limit: '每月限额',
announcement: '公告内容',
announcement_enabled: '公告开关',
name: '名称',
monthly_seconds_limit: '月额度',
total_seconds_pool: '秒数池',
is_active: '状态',
daily_seconds_limit: '每日限额',
username: '用户名',
email: '邮箱',
role: '角色',
};
function formatVal(val: unknown): string {
if (val === true) return '开启';
if (val === false) return '关闭';
if (val === '' || val === null || val === undefined) return '(空)';
return String(val);
}
function renderChanges(before: Record<string, unknown> | null, after: Record<string, unknown> | null) {
if (!before && !after) return '-';
const fields = new Set([...Object.keys(before || {}), ...Object.keys(after || {})]);
if (fields.size === 0) return '-';
return (
<div className={styles.changeDetail}>
{[...fields].map((field) => {
const oldVal = before?.[field];
const newVal = after?.[field];
if (oldVal === undefined && newVal !== undefined) {
return (
<div key={field} className={styles.changeItem}>
<span className={styles.changeField}>{field}:</span>
<span className={styles.changeNew}>{String(newVal)}</span>
</div>
);
}
if (oldVal !== undefined && newVal !== undefined && String(oldVal) !== String(newVal)) {
return (
<div key={field} className={styles.changeItem}>
<span className={styles.changeField}>{field}:</span>
<span className={styles.changeOld}>{String(oldVal)}</span>
<span className={styles.changeArrow}>&rarr;</span>
<span className={styles.changeNew}>{String(newVal)}</span>
</div>
);
}
if (oldVal === undefined && newVal === undefined) return null;
// Same value, show as-is for create actions
if (oldVal === undefined) {
return (
<div key={field} className={styles.changeItem}>
<span className={styles.changeField}>{field}:</span>
<span className={styles.changeNew}>{String(newVal)}</span>
</div>
);
}
return null;
})}
</div>
);
const items: JSX.Element[] = [];
for (const field of fields) {
const oldVal = before?.[field];
const newVal = after?.[field];
const label = FIELD_LABELS[field] || field;
if (oldVal === undefined && newVal !== undefined) {
items.push(
<div key={field} className={styles.changeItem}>
<span className={styles.changeField}>{label}:</span>
<span className={styles.changeNew}>{formatVal(newVal)}</span>
</div>
);
} else if (oldVal !== undefined && newVal !== undefined && formatVal(oldVal) !== formatVal(newVal)) {
items.push(
<div key={field} className={styles.changeItem}>
<span className={styles.changeField}>{label}:</span>
<span className={styles.changeOld}>{formatVal(oldVal)}</span>
<span className={styles.changeArrow}>&rarr;</span>
<span className={styles.changeNew}>{formatVal(newVal)}</span>
</div>
);
}
}
return items.length > 0
? <div className={styles.changeDetail}>{items}</div>
: <span style={{ color: '#8b8ea8' }}></span>;
}
export function AuditLogsPage() {