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://)` }) }, })