Files
Mycontent/miniprogram/pages/super-article-mine/super-article-mine.js
2026-05-09 18:46:59 +08:00

267 lines
7.9 KiB
JavaScript

/**
* Soul创业派对 - 我的动态(含待审/通过/驳回,驳回可跳转编辑)
*/
const app = getApp()
const PAGE_SIZE = 10
const THUMB_MAX = 3
function relativeTime(iso) {
if (!iso) return ''
const d = new Date(iso)
const t = d.getTime()
if (Number.isNaN(t)) return ''
const sec = Math.max(0, Math.floor((Date.now() - t) / 1000))
if (sec < 60) return '刚刚'
if (sec < 3600) return `${Math.floor(sec / 60)}分钟前`
if (sec < 86400) return `${Math.floor(sec / 3600)}小时前`
if (sec < 86400 * 7) return `${Math.floor(sec / 86400)}天前`
const m = `${d.getMonth() + 1}`.padStart(2, '0')
const day = `${d.getDate()}`.padStart(2, '0')
return `${m}-${day}`
}
function auditLabel(st) {
const s = String(st || '').toLowerCase()
if (s === 'pending') return '审核中'
if (s === 'rejected') return '已驳回'
return '已发布'
}
function parseImagesField(images) {
if (images == null) return []
if (typeof images === 'string') {
const s = images.trim()
if (!s) return []
try {
const p = JSON.parse(s)
return Array.isArray(p) ? p : []
} catch (_) {
return []
}
}
return Array.isArray(images) ? images : []
}
function normalizeArticleImageSrc(raw) {
const u = String(raw || '').trim()
if (!u || u === 'undefined' || u === 'null') return ''
if (/^https?:\/\//i.test(u)) return u
if (u.startsWith('//')) return `https:${u}`
if (u.startsWith('wxfile://') || u.startsWith('cloud://')) return u
const base = String((app.globalData && app.globalData.baseUrl) || '').replace(/\/$/, '')
if (u.startsWith('/') && base) return `${base}${u}`
return u
}
function buildThumbSlots(imagesField) {
const rawList = parseImagesField(imagesField)
const n = Math.min(rawList.length, THUMB_MAX)
const thumbSlots = []
for (let i = 0; i < n; i++) {
const srcNorm = normalizeArticleImageSrc(String(rawList[i] || '').trim())
const ok =
!!srcNorm &&
(/^https?:\/\//i.test(srcNorm) ||
srcNorm.startsWith('wxfile://') ||
srcNorm.startsWith('cloud://'))
thumbSlots.push({ slotIdx: i, src: ok ? srcNorm : '', loadFailed: false })
}
return {
showThumbRow: rawList.length > 0,
thumbSlots,
thumbMoreCount: rawList.length > THUMB_MAX ? rawList.length - THUMB_MAX : 0,
}
}
function mapRows(rows) {
return (rows || []).map((r) => {
const st = String(r.auditStatus || 'approved').toLowerCase()
const thumb = buildThumbSlots(r.images)
return Object.assign({}, r, {
auditStatus: st === 'pending' || st === 'rejected' ? st : 'approved',
auditLabel: auditLabel(st),
timeText: relativeTime(r.createdAt),
preview: String(r.preview || '').trim(),
rejectReason: String(r.rejectReason || '').trim(),
showThumbRow: thumb.showThumbRow,
thumbSlots: thumb.thumbSlots,
thumbMoreCount: thumb.thumbMoreCount,
})
})
}
Page({
data: {
statusBarHeight: 44,
auditMode: false,
list: [],
page: 1,
total: 0,
hasMore: true,
loading: true,
loadingMore: false,
},
onLoad() {
this.setData({
statusBarHeight: app.globalData.statusBarHeight || 44,
auditMode: !!app.globalData.auditMode,
})
try {
app.getAuditMode && app.getAuditMode().catch(() => {})
} catch (_) {}
this.reload()
},
onShow() {
this.setData({ auditMode: !!app.globalData.auditMode })
},
goCompose() {
if (app.globalData.auditMode) {
wx.showToast({ title: '当前为体验版,暂无法访问', icon: 'none' })
return
}
wx.navigateTo({ url: '/pages/super-article-editor/super-article-editor' })
},
onReachBottom() {
this.loadMore()
},
goBack() {
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
},
noop() {},
openDetail(e) {
const id = e.currentTarget?.dataset?.id
if (!id) return
wx.navigateTo({ url: `/pages/read/read?superArticleId=${encodeURIComponent(String(id))}` })
},
openEdit(e) {
const id = e.currentTarget?.dataset?.id
if (!id) return
wx.navigateTo({ url: `/pages/super-article-editor/super-article-editor?id=${encodeURIComponent(String(id))}` })
},
tapDeleteArticle(e) {
const id = e.currentTarget?.dataset?.id
if (!id || this._articleDeleting) return
const userId = app.globalData.userInfo?.id
if (!userId) {
wx.showToast({ title: '请先登录', icon: 'none' })
return
}
wx.showModal({
title: '删除动态',
content: '删除后不可恢复,确定删除?',
confirmText: '删除',
confirmColor: '#dc2626',
success: async (r) => {
if (!r.confirm) return
this._articleDeleting = true
wx.showLoading({ title: '删除中…', mask: true })
try {
await app.request({
url: `/api/miniprogram/super/articles/${encodeURIComponent(String(id))}?userId=${encodeURIComponent(String(userId))}`,
method: 'DELETE',
})
wx.hideLoading()
this._articleDeleting = false
const list = this.data.list.filter((x) => String(x.id) !== String(id))
const total = Math.max(0, (Number(this.data.total) || 0) - 1)
this.setData({
list,
total,
hasMore: list.length < total,
})
wx.showToast({ title: '已删除', icon: 'success' })
} catch (_) {
wx.hideLoading()
this._articleDeleting = false
/* app.request 已 toast */
}
},
})
},
onThumbError(e) {
const rowIdx = Number(e.currentTarget.dataset.rowidx)
const slotIdx = Number(e.currentTarget.dataset.slotidx)
if (Number.isNaN(rowIdx) || Number.isNaN(slotIdx)) return
this.setData({ [`list[${rowIdx}].thumbSlots[${slotIdx}].loadFailed`]: true })
},
previewRowImages(e) {
const rowIdx = Number(e.currentTarget.dataset.rowidx)
if (Number.isNaN(rowIdx)) return
const row = this.data.list[rowIdx]
if (!row) return
const urls = parseImagesField(row.images)
.map((u) => normalizeArticleImageSrc(String(u || '').trim()))
.filter((u) => /^https?:\/\//i.test(u))
if (!urls.length) return
const current = String(e.currentTarget.dataset.current || '').trim() || urls[0]
wx.previewImage({ urls, current })
},
async reload() {
const userId = app.globalData.userInfo?.id
if (!userId || !app.globalData.isLoggedIn) {
wx.showToast({ title: '请先登录', icon: 'none' })
setTimeout(() => wx.navigateBack(), 400)
return
}
this.setData({ loading: true, page: 1, list: [], hasMore: true })
try {
const res = await app.request({
url: `/api/miniprogram/super/articles/mine?userId=${encodeURIComponent(String(userId))}&page=1&pageSize=${PAGE_SIZE}`,
silent: true,
})
const raw = Array.isArray(res?.list) ? res.list : []
const list = mapRows(raw)
const total = Number(res?.total) || 0
this.setData({
list,
total,
hasMore: list.length < total,
loading: false,
page: 1,
})
} catch (_) {
this.setData({ loading: false, list: [] })
}
},
async loadMore() {
if (this.data.loading || this.data.loadingMore || !this.data.hasMore) return
const userId = app.globalData.userInfo?.id
if (!userId) return
const nextPage = this.data.page + 1
this.setData({ loadingMore: true })
try {
const res = await app.request({
url: `/api/miniprogram/super/articles/mine?userId=${encodeURIComponent(String(userId))}&page=${nextPage}&pageSize=${PAGE_SIZE}`,
silent: true,
})
const raw = Array.isArray(res?.list) ? res.list : []
const chunk = mapRows(raw)
const merged = this.data.list.concat(chunk)
const total = Number(res?.total) || this.data.total
this.setData({
list: merged,
page: nextPage,
total,
hasMore: merged.length < total,
loadingMore: false,
})
} catch (_) {
this.setData({ loadingMore: false })
}
},
})