const { tryLoginRenewal } = require('../../utils/subscribe'); const { uploadReference } = require('../../utils/file-media'); const { getLayout } = require('../../utils/layout'); // pages/publish/publish.js - 统一发布页(增强版编辑器) const { api, API_BASE, uploadFileCompat } = require('../../utils/request'); const { createSwipeBack } = require('../../utils/util'); const { guardStorageSpace } = require('../../utils/storage-guard'); var MOOD_OPTIONS = ['😊', '🥰', '😢', '😤', '😴', '🤩', '🥺', '😎', '🤔', '😭', '😋', '😌']; var WEATHER_OPTIONS = ['☀️', '⛅', '☁️', '🌧️', '⛈️', '❄️', '🌤️', '🌥️', '🌈', '🌪️', '🌫️', '🌙']; var TYPE_CONFIG = { timeline: { title: '添加记忆日', btn: '保存记忆日', api: 'timelineAdd' }, diary: { title: '写日记', btn: '发布日记', api: 'diaryAdd' }, album: { title: '创建相册', btn: '创建相册', api: 'albumAdd' }, song: { title: '收藏歌曲', btn: '保存歌曲', api: 'songsAdd' }, checkin: { title: '添加打卡', btn: '保存打卡', api: 'checkinAdd' } }; Page({ onLoginSubscribeTap: function() { return tryLoginRenewal(); }, onResize: function() { this.setData(getLayout()); }, data: { type: '', titleText: '', btnText: '', form: {}, editorContent: '', tempImages: [], coverPreview: '', coverPath: '', submitting: false, editLoading:false, editLoadError:false, checkinTypes: ['双方打卡', '第一视角(我)', '她打卡'], checkinTypeIdx: 0, checkinTypeMap: ['both', 'first', 'her'], checkinFreqs: ['每天打卡', '每周打卡', '每月打卡', '不限次数'], checkinFreqIdx: 0, checkinFreqMap: ['daily', 'weekly', 'monthly', 'unlimited'], repeatOptions: ['无重复', '每周重复', '每月重复', '每年重复'], repeatOptionIdx: 0, repeatOptionMap: ['', 'week', 'month', 'year'], editId: 0, // 背景图 bgImageUrl: '', bgImagePath: '', // 心情/天气 moodOptions: MOOD_OPTIONS, weatherOptions: WEATHER_OPTIONS, contentLength: 0 }, onLoad: function(options) { this.setData(getLayout()); var type = options.type || 'timeline'; var editId = options.edit ? String(options.edit) : ""; var cfg = TYPE_CONFIG[type] || TYPE_CONFIG['timeline']; var titleText = editId ? '编辑事件' : cfg.title; var btnText = editId ? '保存修改' : cfg.btn; this.setData({ type: type, titleText: titleText, btnText: btnText, editId: editId }); var self = this; var swipe = createSwipeBack(function() { self.goBack(); }); this.onTouchStart = swipe.onTouchStart; this.onTouchEnd = swipe.onTouchEnd; // 初始化表单 this.resetForm(type); // 非编辑模式:优先恢复本地草稿(防误退出丢失内容) if (!editId) { this.loadDraft(type); } // 如果是编辑模式,加载已有数据 if (editId && type === 'timeline') { this.loadEditData(editId); } // 如果是打卡页,加载情侣昵称 if (type === 'checkin') { this.loadCoupleNames(); } }, // ===== 草稿缓存 ===== // 读取本地草稿并填充表单 loadDraft: function(type) { try { var draftKey = 'publishDraft_' + type; var draft = wx.getStorageSync(draftKey); if (!draft) return; var data = JSON.parse(draft); var patch = {}; if (data.form) { var form = this.data.form; for (var k in data.form) { if (data.form.hasOwnProperty(k) && form.hasOwnProperty(k)) { form[k] = data.form[k]; } } patch.form = form; patch.editorContent = form.content || ''; patch.contentLength = (form.content || '').length; } if (typeof data.repeatOptionIdx === 'number') patch.repeatOptionIdx = data.repeatOptionIdx; if (typeof data.checkinTypeIdx === 'number') patch.checkinTypeIdx = data.checkinTypeIdx; if (typeof data.checkinFreqIdx === 'number') patch.checkinFreqIdx = data.checkinFreqIdx; if (data.bgImageUrl) patch.bgImageUrl = data.bgImageUrl; if (data.coverPreview) { patch.coverPreview = data.coverPreview; patch.coverPath = data.coverPath || data.coverPreview; } this.setData(patch); } catch(e) {} }, // 连续输入只保留一个草稿写入任务,避免每个按键同步写存储。 _saveDraft: function() { this._draftCleared = false; clearTimeout(this._draftTimer); this._draftTimer = setTimeout(() => { this._draftTimer = null; this._persistDraft(); }, 500); }, _flushDraft: function() { clearTimeout(this._draftTimer); this._draftTimer = null; if (!this._draftCleared) this._persistDraft(); }, onHide: function() { this._flushDraft(); }, onUnload: function() { this._flushDraft(); }, _persistDraft: function() { try { var type = this.data.type; if (this.data.editId) return; // 编辑模式不保存草稿 var draftKey = 'publishDraft_' + type; var data = { form: this.data.form, repeatOptionIdx: this.data.repeatOptionIdx, checkinTypeIdx: this.data.checkinTypeIdx, checkinFreqIdx: this.data.checkinFreqIdx, bgImageUrl: this.data.bgImageUrl, coverPreview: this.data.coverPreview, coverPath: this.data.coverPath, time: Date.now() }; wx.setStorageSync(draftKey, JSON.stringify(data)); } catch(e) {} }, // 发布成功后清除草稿 _clearDraft: function() { clearTimeout(this._draftTimer); this._draftTimer = null; this._draftCleared = true; try { var type = this.data.type; wx.removeStorageSync('publishDraft_' + type); } catch(e) {} }, retryEdit: function() { this.loadEditData(this.data.editId); }, loadEditData: async function(id) { this.setData({editLoading:true,editLoadError:false}); try { var item = await api.timelineDetail(id); if (item) { var form = { title: item.title || '', date: item.event_date || '', location: item.location || '', content: item.content || '', repeat_interval: parseInt(item.repeat_interval) || 0, repeat_unit: item.repeat_unit || '' }; // 加载背景图 var bgPath = item.bg_image_file_id || item.bg_image || ''; var bgUrl = bgPath ? require('../../utils/request').getImageUrl(bgPath) : ''; var repeatOptionIdx = 0; if (form.repeat_interval > 0 && form.repeat_unit) { var opts = this.data.repeatOptionMap; for (var i = 0; i < opts.length; i++) { if (opts[i] === form.repeat_unit) { repeatOptionIdx = i; break; } } } this.setData({ form: form, editorContent: form.content || '', repeatOptionIdx: repeatOptionIdx, bgImageUrl: bgUrl, bgImagePath: bgPath }); } } catch(e) { console.error('加载编辑数据失败', e); this.setData({editLoadError:true}); } finally { this.setData({editLoading:false}); } }, loadCoupleNames: async function() { try { var info = await api.coupleInfo(); var myUid = wx.getStorageSync('userId'); var p1 = info.partner1 || {}, p2 = info.partner2 || {}; var me = (p1.id == myUid) ? p1 : p2; var partner = (p1.id == myUid) ? p2 : p1; var myName = me.nickname || '我'; var partnerName = partner.nickname || 'TA'; this.setData({ checkinTypes: ['双方打卡', myName, partnerName] }); } catch(e) { console.error('获取情侣信息失败', e); } }, resetForm: function(type) { var form = {}; if (type === 'diary') { form = { title: '', content: '', mood: '😊', weather: '☀️', lat: 0, lng: 0, location: '' }; } else if (type === 'song') { form = { song_name: '', artist: '', note: '' }; } else if (type === 'album') { form = { title: '', desc: '', lat: 0, lng: 0, location: '' }; this.setData({ coverPreview: '', coverPath: '' }); } else if (type === 'checkin') { form = { title: '', type: 'both', frequency: 'daily' }; this.setData({ checkinTypeIdx: 0, checkinFreqIdx: 0 }); } else { form = { title: '', date: '', location: '', content: '', repeat_interval: 0, repeat_unit: '' }; } this.setData({ form: form, editorContent: form.content || '', tempImages: [], repeatOptionIdx: 0, contentLength: (form.content || '').length }); }, // ===== 自动定位(日记 / 相册发布时自动获取经纬度)===== _autoLocate: function() { var self = this; return new Promise(function(resolve) { // 已有位置则直接使用,避免重复定位 if (self.data.form && self.data.form.lat && self.data.form.lng) { resolve({ lat: self.data.form.lat, lng: self.data.form.lng, location: self.data.form.location || '' }); return; } wx.getFuzzyLocation({ success: function(res) { resolve({ lat: res.latitude, lng: res.longitude, location: '' }); }, fail: function() { // 定位失败不阻塞发布,经纬度置 0 resolve({ lat: 0, lng: 0, location: '' }); } }); }); }, // ===== 通用输入(自动保存草稿)===== onTitleInput: function(e) { this.setData({ 'form.title': e.detail.value }); this._saveDraft(); }, onDateInput: function(e) { this.setData({ 'form.date': e.detail.value }); this._saveDraft(); }, onContentInput: function(e){ var val = e.detail.value || ''; // 正文只存于逻辑层,避免每次敲字回传长文本影响原生输入。 this.data.form.content = val; this.setData({ contentLength: val.length }); this._saveDraft(); }, onLocationInput: function(e){ this.setData({ 'form.location': e.detail.value }); this._saveDraft(); }, onDescInput: function(e) { this.setData({ 'form.desc': e.detail.value }); this._saveDraft(); }, onArtistInput: function(e) { this.setData({ 'form.artist': e.detail.value }); this._saveDraft(); }, onNoteInput: function(e) { this.setData({ 'form.note': e.detail.value }); this._saveDraft(); }, onCheckinTypeChange: function(e) { var idx = parseInt(e.detail.value); this.setData({ checkinTypeIdx: idx, 'form.type': this.data.checkinTypeMap[idx] }); this._saveDraft(); }, onCheckinFreqChange: function(e) { var idx = parseInt(e.detail.value); this.setData({ checkinFreqIdx: idx, 'form.frequency': this.data.checkinFreqMap[idx] }); this._saveDraft(); }, onRepeatChange: function(e) { var idx = parseInt(e.detail.value); var unit = this.data.repeatOptionMap[idx]; this.setData({ repeatOptionIdx: idx, 'form.repeat_interval': unit ? 1 : 0, 'form.repeat_unit': unit || '' }); this._saveDraft(); }, // ===== 封面图片选择(相册)===== chooseCover: function() { var self = this; guardStorageSpace(0).then(function(ok) { if (!ok) return; wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: function(res) { self.setData({ coverPreview: res.tempFilePaths[0], coverPath: res.tempFilePaths[0] }); self._saveDraft(); } }); }); }, // ===== 图片选择(日记)===== chooseImages: function() { var self = this; guardStorageSpace(0).then(function(ok) { if (!ok) return; wx.chooseImage({ count: 9 - self.data.tempImages.length, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: function(res) { self.setData({ tempImages: self.data.tempImages.concat(res.tempFilePaths) }); self._saveDraft(); } }); }); }, // ===== 删除单张图片 ===== removeImage: function(e) { var index = parseInt(e.currentTarget.dataset.index); var images = this.data.tempImages; images.splice(index, 1); this.setData({ tempImages: images }); this._saveDraft(); }, // ===== 提交 ===== submit: async function() { if (this.data.editLoading || this.data.editLoadError) return; if (this.data.submitting) return; this.setData({ submitting: true }); var editId = this.data.editId; try { var type = this.data.type; var form = this.data.form; // 先校验表单 if (type === 'timeline') { if (!form.title) { throw new Error('请填写事件内容'); } if (!form.date) { throw new Error('请填写事件日期'); } } else if (type === 'diary') { if (!form.content || !form.content.trim()) { throw new Error('请填写日记内容'); } } else if (type === 'album') { if (!form.title) { throw new Error('请填写相册名称'); } } else if (type === 'song') { if (!form.song_name || !form.artist) { throw new Error('请填写歌曲名和歌手名'); } } else if (type === 'checkin') { if (!form.title) { throw new Error('请填写打卡标题'); } } // 再执行异步操作(上传/API调用) if (type === 'timeline') { var editId = this.data.editId; // 上传背景图 var bgImagePath = this.data.bgImagePath; if (this.data.bgImageUrl && !bgImagePath) { wx.showLoading({ title: '上传背景图...' }); try { var uploadRes = await uploadFileCompat({ url: API_BASE + '/upload', filePath: this.data.bgImageUrl, name: 'file', formData: { sub_dir: 'timeline_bg' } }); bgImagePath = uploadReference(uploadRes); } finally { wx.hideLoading(); } } var result; if (editId) { result = await api.timelineUpdate({ id: editId, title: form.title, event_date: form.date, location: form.location || '', content: form.content || '', repeat_interval: form.repeat_interval || 0, repeat_unit: form.repeat_unit || '', bg_image: bgImagePath || '' }); } else { result = await api.timelineAdd({ title: form.title, event_date: form.date, location: form.location || '', content: form.content || '', repeat_interval: form.repeat_interval || 0, repeat_unit: form.repeat_unit || '', bg_image: bgImagePath || '' }); } } else if (type === 'diary') { var today = new Date(); var ds = today.getFullYear() + '-' + String(today.getMonth()+1).padStart(2,'0') + '-' + String(today.getDate()).padStart(2,'0'); var uploadPaths = []; if (this.data.tempImages.length > 0) { wx.showLoading({ title: '上传图片中...' }); for (var i = 0; i < this.data.tempImages.length; i++) { var res = await this._uploadDiaryImage(this.data.tempImages[i]); if (res) uploadPaths.push(res); } wx.hideLoading(); } // 从缓存获取用户地区 var region = wx.getStorageSync('region') || ''; // 自动定位获取经纬度(发布后自动显示地理位置) var loc = await this._autoLocate(); await api.diaryAdd({ title: form.title || '', diary_date: ds, content: form.content, images: uploadPaths, region: region, lat: loc.lat, lng: loc.lng, location: loc.location }); } else if (type === 'album') { var coverUrl = ''; if (this.data.coverPath) { wx.showLoading({ title: '上传封面...' }); coverUrl = await this._uploadAlbumCover(this.data.coverPath); wx.hideLoading(); } // 自动定位获取经纬度(发布后自动显示地理位置) var albumLoc = await this._autoLocate(); await api.albumAdd({ title: form.title, description: form.desc || '', cover: coverUrl, lat: albumLoc.lat, lng: albumLoc.lng, location: albumLoc.location }); } else if (type === 'song') { await api.songsAdd({ song_name: form.song_name, artist: form.artist, note: form.note || '' }); } else if (type === 'checkin') { await api.checkinAdd({ title: form.title, type: form.type || 'dual', frequency: form.frequency || 'daily' }); } // 发布/修改成功后清除草稿 this._clearDraft(); wx.showToast({ title: editId ? '修改成功 ✨' : '发布成功 ✨', icon: 'success' }); // 延迟返回页面,给订阅弹窗留出时间 setTimeout(function() { wx.navigateBack(); }, 1500); } catch (e) { wx.hideLoading(); wx.showToast({ title: (e && (e.error || e.message || e.errMsg)) || '发布失败', icon: 'none', duration: 2000 }); } finally { this.setData({ submitting: false }); } }, _uploadAlbumCover: async function(filePath) { return this._uploadMedia(filePath, 'albums'); }, _uploadDiaryImage: async function(filePath) { return this._uploadMedia(filePath, 'diaries'); }, _uploadMedia: async function(filePath, subDir) { var data = await uploadFileCompat({ url: API_BASE + '/upload', filePath: filePath, name: 'file', formData: { sub_dir: subDir } }); return uploadReference(data); }, // ===== 背景图 ===== chooseBgImage: function() { var self = this; guardStorageSpace(0).then(function(ok) { if (!ok) return; wx.chooseImage({ count: 1, sizeType: ['compressed'], sourceType: ['album', 'camera'], success: function(res) { var filePath = res.tempFilePaths[0]; self.setData({ bgImageUrl: filePath, bgImagePath: '' }); } }); }); }, removeBgImage: function() { this.setData({ bgImageUrl: '', bgImagePath: '' }); }, goBack: function() { wx.navigateBack(); } });