- Added checks for audit mode in VIP navigation and member detail pages, preventing access and displaying appropriate messages. - Updated UI elements to conditionally render based on audit mode, enhancing user experience by hiding VIP-related options when in audit mode. - Introduced loading indicators on the VIP page to manage user expectations during content validation. - Enhanced error handling for image uploads in the article editor, ensuring a smoother content creation process. This update aims to improve the overall user experience by enforcing access restrictions and providing clear feedback in audit mode.
223 lines
6.6 KiB
JavaScript
223 lines
6.6 KiB
JavaScript
const app = getApp()
|
|
|
|
Page({
|
|
data: {
|
|
statusBarHeight: 44,
|
|
// 'input' → 'generating' → 'preview' → 'publishing'
|
|
step: 'input',
|
|
|
|
// 输入阶段
|
|
description: '',
|
|
images: [], // [{tempPath, url, uploading, error}]
|
|
|
|
// 预览/编辑阶段
|
|
title: '',
|
|
content: '',
|
|
|
|
genError: '',
|
|
meNickname: '',
|
|
},
|
|
|
|
onLoad() {
|
|
this.setData({
|
|
statusBarHeight: app.globalData.statusBarHeight || 44,
|
|
meNickname: String(app.globalData.userInfo?.nickname || '我').trim() || '我',
|
|
})
|
|
this._checkIdentity()
|
|
},
|
|
|
|
async _checkIdentity() {
|
|
const userId = app.globalData.userInfo?.id
|
|
if (!app.globalData.isLoggedIn || !userId) {
|
|
wx.showToast({ title: '请先登录', icon: 'none' })
|
|
setTimeout(() => wx.navigateBack(), 400)
|
|
return
|
|
}
|
|
try {
|
|
const res = await app.request({
|
|
url: `/api/miniprogram/my/super-stats?userId=${encodeURIComponent(userId)}`,
|
|
silent: true,
|
|
})
|
|
if (!(res?.success && res.data?.isSuperIndividual === true)) {
|
|
wx.showToast({ title: '仅超级个体可发文章', icon: 'none' })
|
|
setTimeout(() => wx.navigateBack(), 400)
|
|
}
|
|
} catch (_) {
|
|
wx.showToast({ title: '身份校验失败', icon: 'none' })
|
|
setTimeout(() => wx.navigateBack(), 400)
|
|
}
|
|
},
|
|
|
|
// ─────────────── 图片管理 ───────────────
|
|
|
|
chooseImages() {
|
|
const remain = 3 - this.data.images.length
|
|
if (remain <= 0) return
|
|
wx.chooseMedia({
|
|
count: remain,
|
|
mediaType: ['image'],
|
|
sourceType: ['album', 'camera'],
|
|
success: (res) => {
|
|
const newImgs = res.tempFiles.map((f) => ({
|
|
tempPath: f.tempFilePath,
|
|
url: '',
|
|
uploading: true,
|
|
error: false,
|
|
}))
|
|
const images = [...this.data.images, ...newImgs]
|
|
this.setData({ images })
|
|
// 并发上传
|
|
newImgs.forEach((img, offset) => {
|
|
const idx = this.data.images.length - newImgs.length + offset
|
|
this._uploadImage(img.tempPath, idx)
|
|
})
|
|
},
|
|
})
|
|
},
|
|
|
|
async _uploadImage(tempPath, idx) {
|
|
const baseUrl = app.globalData.baseUrl || ''
|
|
const token = wx.getStorageSync('token') || ''
|
|
return new Promise((resolve) => {
|
|
wx.uploadFile({
|
|
url: `${baseUrl}/api/upload`,
|
|
filePath: tempPath,
|
|
name: 'file',
|
|
formData: { folder: 'article-images' },
|
|
header: token ? { Authorization: `Bearer ${token}` } : {},
|
|
success: (res) => {
|
|
try {
|
|
const parsed = JSON.parse(res.data)
|
|
if (parsed.success && parsed.url) {
|
|
this._updateImage(idx, { url: parsed.url, uploading: false, error: false })
|
|
resolve(parsed.url)
|
|
return
|
|
}
|
|
} catch (_) { /* ignore */ }
|
|
this._updateImage(idx, { uploading: false, error: true })
|
|
resolve('')
|
|
},
|
|
fail: () => {
|
|
this._updateImage(idx, { uploading: false, error: true })
|
|
resolve('')
|
|
},
|
|
})
|
|
})
|
|
},
|
|
|
|
_updateImage(idx, patch) {
|
|
const images = [...this.data.images]
|
|
if (images[idx]) {
|
|
images[idx] = { ...images[idx], ...patch }
|
|
this.setData({ images })
|
|
}
|
|
},
|
|
|
|
removeImage(e) {
|
|
const idx = Number(e.currentTarget.dataset.index)
|
|
const images = this.data.images.filter((_, i) => i !== idx)
|
|
this.setData({ images })
|
|
},
|
|
|
|
// ─────────────── 输入 ───────────────
|
|
|
|
onDescInput(e) {
|
|
this.setData({ description: e.detail.value || '' })
|
|
},
|
|
|
|
onTitleInput(e) {
|
|
this.setData({ title: e.detail.value || '' })
|
|
},
|
|
|
|
onContentInput(e) {
|
|
this.setData({ content: e.detail.value || '' })
|
|
},
|
|
|
|
// ─────────────── AI 生成 ───────────────
|
|
|
|
async generateArticle() {
|
|
if (this.data.step === 'generating') return
|
|
const desc = String(this.data.description || '').trim()
|
|
if (!desc) {
|
|
wx.showToast({ title: '请先填写描述', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
// 等待所有图片上传完成
|
|
const stillUploading = this.data.images.some((img) => img.uploading)
|
|
if (stillUploading) {
|
|
wx.showToast({ title: '图片上传中,请稍候', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
const imageUrls = this.data.images.filter((img) => img.url).map((img) => img.url)
|
|
const userId = app.globalData.userInfo?.id || ''
|
|
|
|
this.setData({ step: 'generating', genError: '' })
|
|
|
|
try {
|
|
const res = await app.request({
|
|
url: '/api/miniprogram/super/articles/generate',
|
|
method: 'POST',
|
|
data: { userId, description: desc, imageUrls },
|
|
timeout: 90000,
|
|
})
|
|
if (!res?.success) throw new Error(res?.error || 'AI 生成失败')
|
|
this.setData({
|
|
step: 'preview',
|
|
title: String(res.data?.title || '').trim(),
|
|
content: String(res.data?.content || '').trim(),
|
|
})
|
|
} catch (err) {
|
|
this.setData({ step: 'input', genError: err.message || 'AI 生成失败,请重试' })
|
|
wx.showToast({ title: this.data.genError, icon: 'none', duration: 2500 })
|
|
}
|
|
},
|
|
|
|
backToInput() {
|
|
this.setData({ step: 'input', genError: '' })
|
|
},
|
|
|
|
// ─────────────── 发布 ───────────────
|
|
|
|
async submitArticle() {
|
|
if (this.data.step === 'publishing') return
|
|
const userId = app.globalData.userInfo?.id
|
|
const title = String(this.data.title || '').trim()
|
|
const content = String(this.data.content || '').trim()
|
|
if (!title || !content) {
|
|
wx.showToast({ title: '标题和正文不能为空', icon: 'none' })
|
|
return
|
|
}
|
|
this.setData({ step: 'publishing' })
|
|
try {
|
|
const res = await app.request({
|
|
url: '/api/miniprogram/super/articles',
|
|
method: 'POST',
|
|
data: { userId, title, content },
|
|
})
|
|
if (!res?.success) throw new Error(res?.error || '发布失败')
|
|
wx.showToast({ title: '发布成功', icon: 'success' })
|
|
setTimeout(() => wx.navigateBack(), 400)
|
|
} catch (e) {
|
|
wx.showToast({ title: e.message || '发布失败', icon: 'none' })
|
|
this.setData({ step: 'preview' })
|
|
}
|
|
},
|
|
|
|
goBack() {
|
|
wx.navigateBack({ fail: () => wx.switchTab({ url: '/pages/my/my' }) })
|
|
},
|
|
|
|
// ─────────────── 手动添加 @自己 / #链接 ───────────────
|
|
|
|
insertMentionSelf() {
|
|
const nick = this.data.meNickname || '我'
|
|
this.setData({ content: `${this.data.content}@${nick} ` })
|
|
},
|
|
|
|
insertHashLink() {
|
|
this.setData({ content: `${this.data.content}#链接(https://)` })
|
|
},
|
|
})
|