Gate record features and storage entry points with invitation guidance; enforce record access in the backend. Add homepage greetings, right-swipe profile dismissal, and move the smoking switch to settings. Simplify API base configuration and remove the unused legacy tab bar component.
484 lines
18 KiB
JavaScript
484 lines
18 KiB
JavaScript
const { withRecordAccess } = require('../../utils/record-access');
|
|
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(withRecordAccess({
|
|
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(); }
|
|
}));
|