// =============================================
// js/render.js — 所有 HTML 產生函式
// =============================================
/* ---- Helpers ---- */
function fmtNum(n) {
if (!n) return '0';
return n >= 1000 ? (n / 1000).toFixed(1) + 'k' : String(n);
}
function fmtDate(s) {
if (!s) return '';
return new Date(s).toLocaleDateString('zh-TW', { year: 'numeric', month: '2-digit', day: '2-digit' });
}
function initials(name) { return name ? name.slice(0, 1) : '?'; }
function avatarColor(str) {
const colors = ['#0e4d8a','#007a6e','#8a5000','#6a1a1a','#4a1a8a'];
let h = 0; for (let c of (str||'')) h = (h * 31 + c.charCodeAt(0)) & 0xffffffff;
return colors[Math.abs(h) % colors.length];
}
function esc(s) { return String(s||'').replace(//g,'>').replace(/"/g,'"'); }
/* ---- Header ---- */
function renderHeader(state) {
if (!state.user) return '';
const pending = (state.pendingCount || 0);
return `
`;
}
/* ---- Login Page ---- */
function renderLogin(tab = 'login') {
return `
${initials(name)}
${esc(name)}
${fmtDate(post.created_at)}
${showStatus ? `
${post.status==='approved'?'已上架':post.status==='pending'?'待審核':'已拒絕'}
` : ''}
${post.content_type==='image'?' 圖片':' 文字'}
${esc(post.title)}
${(post.tags||[]).map((t,i)=>`# ${esc(t)}`).join('')}
提示詞:${esc(post.prompt)}
${thumb ? `
` : ''}
`;
}
/* ---- Feed Page ---- */
function renderFeed(state) {
const { posts = [], sortBy = 'view_count', filterTag, searchQ = '', stats = {}, topPosts = [], likedSet, profile } = state;
const maxViews = Math.max(...posts.map(p => p.view_count || 0), 1);
const allTags = [...new Set(posts.flatMap(p => p.tags || []))];
const typeFilters = [
{ key: null, label: '全部', icon: 'fa-border-all' },
{ key: 'image', label: '圖像', icon: 'fa-image' },
{ key: 'text', label: '文字', icon: 'fa-align-left' },
];
const toolFilters = ['ChatGPT','Midjourney','DALL-E','Claude','Stable Diffusion','Gemini'];
return `
我的分享
共 ${myPosts.length} 篇
${state.loading
? `
載入中…
`
: myPosts.length === 0
? `
`
: myPosts.map(p => renderPostCard(p, { maxViews, likedSet, showStatus: true })).join('')
}
`;
}
/* ---- Admin Page ---- */
function renderAdmin(state) {
const { adminPosts = [], adminTab = 'pending', adminStats = {} } = state;
return `
審核管理後台
${[
{ label:'待審核', val: adminStats.pending||0, cls:'val-gold' },
{ label:'已上架', val: adminStats.approved||0, cls:'val-teal' },
{ label:'已拒絕', val: adminStats.rejected||0, cls:'val-coral' },
{ label:'會員人數', val: adminStats.members||0, cls:'val-blue' },
].map(s=>`
`).join('')}
${state.loading
? `
載入中…
`
: adminPosts.length === 0
? `
`
: adminPosts.map(p => {
const name = p.profiles?.name || '未知';
const flags = p.sensitive_flags || [];
return `
${esc(p.title)}
${p.status==='approved'?'已上架':p.status==='pending'?'待審核':'已拒絕'}
提交者:${esc(name)} · ${fmtDate(p.created_at)} · ${p.content_type==='image'?'圖片':'文字'}
${flags.length > 0 ? `
偵測到敏感詞:${flags.map(esc).join('、')}
` : ''}
提示詞:${esc(p.prompt)}
${(p.tags||[]).map(t=>`# ${esc(t)}`).join('')}
${p.status === 'pending' ? `
` : ''}
`;
}).join('')
}
`;
}
/* ---- Upload Modal ---- */
function renderUploadModal() {
return `